マンガでわかるJavaScriptのPromise

第9話「asyncとawait」

今回は「Promise」の処理を簡便に書く、「async」と「await」について説明します。

コード1

 「async/await」を使って、短く書く例です。

ソースコード)「async/await」の利用

const timer = (time, text) => new Promise(resolve => {
    setTimeout(() => {
        console.log(text);
        resolve();
    }, time);
});

const func = async function() {
    await timer(500, '処理1');
    await timer(600, '処理2');
    await timer(700, '処理3');
    await timer(800, '処理4');
    console.log('終了');
};
func();

    

出力)実行結果

処理1
処理2
処理3
処理4
終了

    

コード2

 「async/await」を使って、短く書く例その2です。「resolve」を使って値を戻します。

ソースコード)「resolve」を使って値を戻す

const timerB = time => new Promise(resolve => {
    setTimeout(() => {
        resolve(`end: ${time}`);
    }, time);
});

const func = async function() {
    const res1 = await timerB(500);
    const res2 = await timerB(600);
    console.log(res1, res2);
};
func();

    

出力)実行結果

end: 500 end: 600

    

コラム:HTMLを表示 2

 「HTMLを表示 1」で書いたコードを、「async/await」を利用して短く書いてみます。

ソースコード)「fetch」関数の利用

(async function() {
    const response = await fetch(location.href);
    if (! response.ok) { return }
    const text = await response.text();
    console.log(text.replace(/>/g, '>\n'));
})();

    

 すっきりと書くことができました。このように短く読みやすくなるので、新しいプログラムを書く時は、「async/await」を使うとよいでしょう。

コラム:配列の処理 1

 このコラムは、ある程度「Promise」に慣れてから読むとよいです。また、配列の反復メソッドが使える中級者以上を対象にしています。

 「async/await」の処理を配列に対しておこなう時は注意が必要です。仕様を理解していないと、期待したとおりには動きません。

 まず以下のような、「Promise」オブジェクトを返す共通コードと、値の配列を用意します。

ソースコード)出力をして値を返す「timerC」関数

const timerC = time => new Promise(resolve => {
    setTimeout(() => {
        console.log(`exec: ${time}`);
        resolve(`resolve: ${time}`);
    }, time);
});

const arr = [700, 600, 500];

    

 続いて、次の2つのコードを見てください。

 配列の各要素に対して、「Promise」を利用した処理を、「await」で待機しながら実行します。そして結果を配列で受け取り、最後に「end all」を出力します。

 1つ目は、よくある間違いのコードです。

ソースコード)間違いのコード

(async function() {
    const res = arr.map(async x => await timerC(x));
    console.log('end all', res);
})();

    

出力)実行結果

end all [Promise, Promise, Promise]
exec: 500
exec: 600
exec: 700

    

 処理末の「end all」が先に出力されています。

 「async」を付けた関数では、対応する関数スコープ内で「await」を待ちながら実行します。このコードの「await」と対応しているのは、内側の「async」(「map」の引数の関数)です。

 またこのコードでは、結果の配列に「Promise」オブジェクトが格納されています。この部分も期待とは違います。

 2つ目は、期待した動作をするコードです。

ソースコード)期待した動作をするコード

(async function() {
    const res = [];
    for (let i = 0; i < arr.length; i ++) {
        res.push(await timerC(arr[i]));
    }
    console.log('end all', res);
})();

    

出力)実行結果

exec: 700
exec: 600
exec: 500
end all ['resolve: 700', 'resolve: 600', 'resolve: 500']

    

 全ての処理が終わったあと「end all」が出力されています。結果の配列にも、期待した値が入っています。「async」と「await」の対応を確認してください。

 さて、後者は期待通り動作しましたが、各「Promise」が終わってから実行するため処理時間が掛かります。通信などでは同時に読み込み、並列的に処理をおこないたいです。

 この処理を並列的に処理する方法を、後の方で出てくるコラム「配列の処理 2」で紹介します。

作成:2022/07/22  更新:2022/07/22  [Permalink]

クロクロ・ショップ
特殊な技術書多数あります
レトロ風RPG フルスクラッチ開発 全コード
レトロゲーム風RPGの全コードを掲載して解説した本
HTML5とJavaScriptで作る 落ち物パズルゲーム 全コード
『テトリス』や『ぷよぷよ』に類似した落ち物パズルゲームの全コードを掲載して解説した本
JavaScriptでリバーシゲームをつくろう
ライブラリを使わずに全コードを書いて、リバーシゲームを作るまでを徹底解説
ワールドマップ自動生成読本
二次元ゲームのワールドマップを自動生成するための本
レトロゲームファクトリー
過去のゲームを最新機に移植する会社のお仕事小説(新潮社)
顔貌売人 ハッカー探偵 鹿敷堂桂馬
プログラマーが探偵役のエンタメ・ミステリー第2弾 最先端の情報犯罪に安藤と鹿敷堂が挑みます(文藝春秋)
裏切りのプログラム ハッカー探偵 鹿敷堂桂馬
プログラマー鹿敷堂桂馬が、女性社長の安藤裕美と共に、IT業界で起きた事件に挑む。2016年の松本清張賞の最終候補作(文藝春秋)
×