kintone async/await による非同期処理メモ

kintone でもasync/await による非同期処理を実装できるようになりました。
※IE11 を考慮しなくて済むため
これまでの Promise 処理から async/await による非同期処理に移行する場合の変更点をメモ書き

# kintone Promise、async/await の基礎知識

kintone Promise は、javaScript 標準のPromiseと同じような機能ですが、IE11 をサポートしていた点が異なる。

- [kintoneにおけるPromiseの書き方の基本](https://developer.cybozu.io/hc/ja/articles/360023047852)
- [mdn Promise](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [mdn await](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/await)


# 基本的な非同期処理

Promise と async/await による非同期処理を比較してみます。
自アプリのレコードを３回取得する処理
※[目指せ！JavaScriptカスタマイズ中級者（２） 〜Promiseのかわりにasync/await編〜](https://developer.cybozu.io/hc/ja/articles/900001244323-%E7%9B%AE%E6%8C%87%E3%81%9B-JavaScript%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA%E4%B8%AD%E7%B4%9A%E8%80%85-%EF%BC%92-Promise%E3%81%AE%E3%81%8B%E3%82%8F%E3%82%8A%E3%81%ABasync-await%E7%B7%A8-)のコードを元にカスタマイズしています。
async/await により非同期処理が分かりやすいですね。

- Promise を使った非同期処理
- Promise を省略した非同期処理
- Promise を省略した非同期処理（アロー関数化、エラー処理追加）
- Promise 非同期処理をまとめて関数化
- async/await による非同期処理
- async/await による非同期処理をまとめて関数化

```test1.js
(() => {
    'use strict';
    const params1 = { app: kintone.app.getId(), query: '$id = 1', fields: ['$id'] };
    const params2 = { app: kintone.app.getId(), query: '$id = 2', fields: ['$id'] };
    const params3 = { app: kintone.app.getId(), query: '$id = 3', fields: ['$id'] };

    // Promise を使った非同期処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], function (event) {
        console.log('Promise-1: Promise を使った非同期処理');
        return new kintone.Promise(function (resolve, reject) {
            return kintone.api('/k/v1/records', 'GET', params1).then(function (resp1) { // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
                console.log(resp1);
                return kintone.api('/k/v1/records', 'GET', params2);
            }).then(function (resp2) { // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
                console.log(resp2);
                return kintone.api('/k/v1/records', 'GET', params3);
            }).then(function (resp3) { // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
                console.log(resp3);
                resolve(event); // resolveでプロミスの処理が終了したことを伝える
            });
        });
    });

    // Promise を省略した非同期処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], function (event) {
        console.log('Promise-2: Promise を省略した非同期処理');
        return kintone.api('/k/v1/records', 'GET', params1).then(function (resp1) { // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
            console.log(resp1);
            return kintone.api('/k/v1/records', 'GET', params2);
        }).then(function (resp2) { // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
            console.log(resp2);
            return kintone.api('/k/v1/records', 'GET', params3);
        }).then(function (resp3) { // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
            console.log(resp3);
            return event; // kintone.Promiseオブジェクト(event)を return することでプロミスの処理が終了したことを伝える
        });
    });

    // Promise を省略した非同期処理（アロー関数化、エラー処理追加）
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], (event) => {
        console.log('Promise-3: Promise を省略した非同期処理（アロー関数化、エラー処理追加）');
        return kintone.api('/k/v1/records', 'GET', params1).then((resp1) => { // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
            console.log(resp1);
            return kintone.api('/k/v1/records', 'GET', params2);
        }).then((resp2) => { // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
            console.log(resp2);
            return kintone.api('/k/v1/records', 'GET', params3);
        }).then((resp3) => { // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
            console.log(resp3);
            return event; // kintone.Promiseオブジェクト(event)を return することでプロミスの処理が終了したことを伝える
        }).catch((e) => {
            alert(e.message);
            return event;
        });
    });

    // 非同期処理をまとめて関数化
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], (event) => {
        console.log('Promise-4: Promise 非同期処理をまとめて関数化');
        const getAppRecords = () => {
            let results = {};
            return kintone.api('/k/v1/records', 'GET', params1).then((resp1) => { // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
                results.resp1 = resp1;
                return kintone.api('/k/v1/records', 'GET', params2);
            }).then((resp2) => { // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
                results.resp2 = resp2;
                return kintone.api('/k/v1/records', 'GET', params3);
            }).then((resp3) => { // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
                results.resp3 = resp3;
                return results; // 各レスポンスをまとめて返す
            });
        }

        return getAppRecords().then((results) => {
            console.log(results);
            return event;
        }).catch((e) => {
            alert(e.message);
            return event;
        });

    });

    // async/await による非同期処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-1: async/await による非同期処理');
        try {
            //          ↓待ちたい処理にawaitをつける
            const resp1 = await kintone.api('/k/v1/records', 'GET', params1); // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
            const resp2 = await kintone.api('/k/v1/records', 'GET', params2); // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
            const resp3 = await kintone.api('/k/v1/records', 'GET', params3); // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
            console.log({ resp1: resp1, resp2: resp2, resp3: resp3 });
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

    // async/await による非同期処理を関数化
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-2: async/await による非同期処理を関数化');
        const getAppRecords = async () => {
            //          ↓待ちたい処理にawaitをつける
            const resp1 = await kintone.api('/k/v1/records', 'GET', params1); // レスポンス内容がresp1に入る。ちゃんと待ってから次の処理に移る
            const resp2 = await kintone.api('/k/v1/records', 'GET', params2); // レスポンス内容がresp2に入る。ちゃんと待ってから次の処理に移る
            const resp3 = await kintone.api('/k/v1/records', 'GET', params3); // レスポンス内容がresp3に入る。ちゃんと待ってから次の処理に移る
            return { resp1: resp1, resp2: resp2, resp3: resp3 };
        }

        try {
            const results = await getAppRecords();
            console.log(results);
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

})();
```
- 実行結果
　どの処理も対象レコードを取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/423b41ac-118a-1ec4-a436-7fde5f4d019f.png)


# async/await による非同期処理を for ループ処理

for ループ処理で、await を指定しても順番に処理される

```.js
(() => {
    'use strict';
    const params1 = { app: kintone.app.getId(), query: '$id = 1', fields: ['$id'] };
    const params2 = { app: kintone.app.getId(), query: '$id = 2', fields: ['$id'] };
    const params3 = { app: kintone.app.getId(), query: '$id = 3', fields: ['$id'] };

    // async/await による非同期処理をループ処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-3: async/await による非同期処理をループ処理');
        try {
            let results = [];
            const params = [params1, params2, params3];
            for (let index = 0; index < params.length; index++) {
                const resp = await kintone.api('/k/v1/records', 'GET', params[index]);
                results.push(resp);
            }
            console.log(results);
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

})();
```

- 実行結果
![2022-07-15_14h52_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/0681deed-9d45-76a2-fa2e-dfe58a55837c.png)

# async/await による非同期処理を forEach ループ処理は、NG

forEach ループ処理で await を行っても、同期処理の終了を待たずに処理が進む。
forEach の戻り値が無いので、Promise オブジェクトが継承されないらしい。

```.js
(() => {
    'use strict';
    const params1 = { app: kintone.app.getId(), query: '$id = 1', fields: ['$id'] };
    const params2 = { app: kintone.app.getId(), query: '$id = 2', fields: ['$id'] };
    const params3 = { app: kintone.app.getId(), query: '$id = 3', fields: ['$id'] };

    // async/await による非同期処理をループ処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-4: async/await による非同期処理をループ処理');
        try {
            let results = [];
            const params = [params1, params2, params3];
            params.forEach(async (param) => {
                const resp = await kintone.api('/k/v1/records', 'GET', param);
                console.log(resp);
                results.push(resp);

            });
            console.log(results);
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

})();
```

- 実行結果
　console.log(results);が空配列で、その後に、console.log(resp);が表示されている
![2022-07-15_15h31_48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/e5f395bf-1c84-3d8f-ccd9-61872aaa1daa.png)

# async/await による非同期処理を map ループ処理

forEach がダメなら、map 関数を試したところ、kintone.Promise.all と組み合わせるとOK
kintone.Promise.all を使うので、あまり await のメリットが感じられない。

```.js
(() => {
    'use strict';
    const params1 = { app: kintone.app.getId(), query: '$id = 1', fields: ['$id'] };
    const params2 = { app: kintone.app.getId(), query: '$id = 2', fields: ['$id'] };
    const params3 = { app: kintone.app.getId(), query: '$id = 3', fields: ['$id'] };

    // async/await による非同期処理をループ処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-4: async/await による非同期処理をループ処理');
        try {
            let results = [];
            const params = [params1, params2, params3];
            const p1 = params.map(async (param) => {
                const resp = await kintone.api('/k/v1/records', 'GET', param);
                console.log(resp);
                results.push(resp);
                return resp;
            });
            await kintone.Promise.all(p1);
            console.log(p1, results);
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

})();
```

- 実行結果
![2022-07-15_15h51_41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/68afcebc-16d1-de3b-bb99-710851323faa.png)

# map ループでパラレル処理して await で非同期処理の完了を待つ

kintone.Promise.all を使って、パラレル処理にしてみる。

```.js
(() => {
    'use strict';
    const params1 = { app: kintone.app.getId(), query: '$id = 1', fields: ['$id'] };
    const params2 = { app: kintone.app.getId(), query: '$id = 2', fields: ['$id'] };
    const params3 = { app: kintone.app.getId(), query: '$id = 3', fields: ['$id'] };

    // async/await による非同期処理をループ処理
    kintone.events.on(['app.record.create.show', 'app.record.edit.show'], async (event) => { // async　をつける
        console.log('await-6: async/await による非同期処理を map ループでパラレル処理');
        try {
            let results = [];
            const params = [params1, params2, params3];
            const p1 = params.map((param) => {
                const resp = kintone.api('/k/v1/records', 'GET', param);
                console.log(resp);
                results.push(resp);
                return resp;
            });
            await kintone.Promise.all(p1);
            console.log(p1, results);
            return event;
        } catch (e) {
            // パラメータが間違っているなどAPI実行時にエラーが発生した場合
            alert(e.message);
            return event;
        }
    });

})();
```
- 実行結果
ネットワークタブを見ると、３つのAPIが同時に処理されているのがわかる
![2022-07-15_15h58_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/d6728222-4775-231a-2c86-107c8e0b5032.png)
![2022-07-15_15h59_35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/5f684935-febb-627d-08bd-3342440207b1.png)