カテゴリー内の他の記事

kintoneにおけるPromiseの書き方の基本

フォローする

(著者:kintone エバンジェリスト 村濱 一樹

今までにJavaScriptカスタマイズをしたことがある方は、「Promise」について聞いたことや使ったことがあると思いますが、
プログラミングに慣れていないと難しい概念だと思います。
すでにPromiseに関する記事はこのcybozu developer network内にいくつか公開されていますが、
「Promiseって結局どういう風に書くんだっけ?」というときに見返していただくために、書き方について重点的にこの記事でまとめたいと思います。

Promiseに関する既存の記事

過去のPromiseに関する記事はこちらです。

Promiseを使う利点

kintoneでPromiseを使う利点は大きく2つあります。

  1. レコード作成時などに、処理を待ってからレコードを保存することができる(同期的処理、と呼びます)
    「あるアプリAのレコードを保存時、アプリBのレコードを取得し、その値を利用」というようにレコードの保存時などにkintone APIを使って他のデータを取得したり変更したり、同期的に処理することができるようになります。
    Promiseに対応しているイベントはこちらを参照ください。
  2. コールバック関数を使った記述と比較してPromiseを用いたほうが簡素に記述できる
    レコード詳細画面表示時などは同期処理は不要なのですが、2における恩恵も大きいので、Promiseに対応しているイベントなどでは基本的にはコールバック関数方式よりもPromise方式をおすすめします。

Promiseの書き方の基本

下記の例を考えてみます。

  • 例)見積もりアプリ(カスタマイズするアプリ)のレコード保存時、商品アプリ(アプリID: 1)から商品A(レコードID: 1)の⾦額を取得し、その値を見積もりアプリに登録する

Promiseを利用する(1回)

  • Promiseをつかって同期処理をする

このように kintone.api() はコールバック関数を省略するとPromiseオブジェクトが返り値になります。

kintone.api('/k/v1/record', 'GET', {app: 1, id: 1}); // これでPromiseオブジェクトが生成される

それを return してあげることによってkintone側で app.record.create.submit 時など、処理を待ってくれる仕組みをkintoneは持っています。Promiseオブジェクトを return しないと処理をまってくれないので注意しましょう。(逆に言えば、レコード詳細ページなど、処理を待たせる必要がなければPromiseオブジェクトの return は必須ではありません。)

また、 Promiseが正常終了した場合は then() で結果を受け取ることができ、
エラーが発生した場合は catch() を使うことでデータ取得に失敗したときなどの制御ができます。

  • thenとcatchを使った例

Promiseを利用する(複数回)

問題はここからです。なんとなくPromiseを使っている方にとっては、結局複数Promiseをやるにはどうしたらいいんだろうと思う方もいらっしゃると思いますので、整理してみます。

  • 例) レコード保存時のイベントで、商品アプリ(アプリID: 1)から商品A, B, C(レコードID: 1, 2, 3)の金額を取得して、その合計を見積もりアプリ(カスタマイズするアプリ)に登録する

前述の例に加え、更に取得するレコードを増やしてみます。実際にはレコード一括取得APIを使えば1回のAPI呼び出しで済みますが、今回はProimseの説明のために1レコードずつ合計3レコード取得します。

  • Promiseをつかって同期処理をする(複数)

Promise は then() で処理を待つことができ、 then() は繰り返し使えます。
すこし難しいかもしれないですが、ここでも大事なのは、Promiseオブジェクトを return しているところです。レコードIDが 1 の部分のみならず、レコードIDが 2 と 3 のところでもPromiseオブジェクトを return しているので、後続の then() 内で、レコードID 23 の取得結果を利用することができます。
また、このように複数回Promiseを行う場合は、上から順番に処理がされます。

  • thenを使って連続して使う方法(上記を抜粋)

  • catchを加えてエラー制御
    エラー制御をするには前述のものと同様、最後に catch() をつけることで可能です。レコード1,2,3のどの取得でエラーが起きてもこの catch() を使ってエラー制御をすることができます。

コールバック関数との比較

上記のように複数のデータを取得するときに特にPromiseは便利です。コールバック関数で複数のデータを取得する場合と比較すると差は明らかです。

  • 例) 商品アプリ(アプリID: 1)から商品A, B, C(レコードID: 1, 2, 3)の金額を取得して、その合計を表示する(コールバック関数ではSubmit時に処理待ちができないので詳細レコード表示時とします)

このように、コールバック関数で書くと、複数APIを呼び出すときに関数が入れ子になってしまい、非常に読みづらくなってきます。エラー処理をいれるとさらに複雑になります。

  • エラー処理も含めた例

上記のように複数のデータを取得したときなど複雑な記述にどうしてもなってしまいますので、コールバック形式よりもPromise形式をぜひ使うようにしましょう。

kintone.Promiseの使い方

同期的処理をさせたい場合は Promiseを return すればよい、と前述しましたが、kintone.api() を使わなくとも明示的にPromiseを返却することもができます。
基本的にはkintone.api()でPromiseオブジェクトをreturnするようにすればいいですが、処理が複雑な場合などはこちらのほうが見通しがいい場合があるので覚えておくとよいでしょう。

ぜひこちらの例も御覧ください。
kintone.Promiseとは

  • kintone.Promise でPromiseオブジェクト作成

動作確認

実際にアプリを用意して、下記コードを実装しました。このようにすることで、レコード保存時に他のアプリと連携することが可能です。

  • アプリの概要

    • 見積もりアプリのレコードを作成時、該当する商品を商品リストからデータ取得する。
    • 今回はPromiseの説明のためルックアップフィールドは使わない
    • イメージ
  • コード

Column1: async/awaitで直感的に同期的処理を書く

Promiseの概念を覚えるのは結構面倒です。最近のJavaScriptではasync/awaitという書き方を使うことでもっと同期的処理をシンプルに書くことができるようになりました。
ただし、IE11など一部ブラウザで動作しませんので、使うときは注意しましょう。
async/awaitの詳細についてはこちら

  • 例) レコード保存時のイベントで、商品アプリ(アプリID: 1)から商品A, B, C(レコードID: 1, 2, 3)の金額を取得して、その合計を見積もりアプリ(カスタマイズするアプリ)に登録する

Column2: よくある間違い

Promiseを利用する際、下記のように書いてしまうことがあるようです。よくある間違いのパターンと修正点を紹介します。

Promiseを使用しているのにコールバック地獄のような状態に陥るコード

  • 間違った例

    上記のように、複数回Promiseを用いる場合に then() の中にまた then() を書いてしまうことで入れ子が深くなってしまうことがあります。
    動作はしますが、わかりにくいので Promiseを利用する(複数回) の項で説明しているように入れ子にせず書くことができます。下記の正しい例と見比べてみてください。

  • 正しい例

Promiseを使用しているのにthenメソッドチェーンができていないコード

  • 間違った例

上記も動作自体はしますが、それぞれのAPI呼び出しが並列に処理されてしまいます。順番に実行したい場合は前述の正しい例のように、 then() の中に次のAPIを呼び出す部分を書く必要があります。

デモ環境

(見積もり) https://dev-demo.cybozu.com/k/309/

※デモ環境についての説明はこちら

このTipsは、2019年2月版 kintoneで確認したものになります。

記事に関するフィードバック

直接的に記事と関連がないご質問はcybozu developer コミュニティをご活用ください。

Avatar
TK

こんにちは、いつもお世話になってます。

Column1のasync awaitのところのサンプルコードの、6~10行目

閉じカッコが一つ多いと思うので確認お願いします。

Avatar
cybozu Development team

TK様

ご指摘ありがとうございます。そのとおりでしたので修正いたしました。

ログインしてコメントを残してください。