以前、JavaScriptのES5仕様におけるPromiseの書き方を記載しましたが、より可読性の高い状態で書けるasync/awaitでの書き方も勉強しておきたいと思います。
(というか、そろそろこの書き方の方がメジャーになっています・・・よね?)
また、async/await構文の書き方に加えて、ES5⇒async/awaitに書き換えるとどの程度見やすくなるのかも確認できればと思います。
ES5仕様の書き方とasync/awaitの書き方で比較
ES5仕様の書き方⇒async/awaitに書き直す、という順番で見て行きます。
直列で実行されるPromise(ES5)
一番シンプルなケースでしょうか。
非同期処理が順番に実行されて欲しい場合。
then()でつなげて行けばOK。
コード
(function() { // asyncFuncは非同期処理。Promiseオブジェクトを返却する。 // 引数sleepにに応じたtimeout処理を行う。 const asyncFunc = function(count, sleep) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log( "asyncFunc " + count + " 回目の実行。\n" + sleep + "ms 待機しました。" ); resolve(); }, sleep); }); } // 非同期処理を直列で実行する。 // timeout処理があるけど、ちゃんと上から順番に実行される? asyncFunc(1, 100) .then(function(response) { return asyncFunc(2, 300); }) .then(function(response) { return asyncFunc(3, 200); }) .catch(function(error) { /* asyncFuncでエラーになった場合の処理 ・ ・ ・ */ }); })();
実行結果
promise.js:7 asyncFunc 1 回目の実行。 100ms 待機しました。 promise.js:7 asyncFunc 2 回目の実行。 300ms 待機しました。 promise.js:7 asyncFunc 3 回目の実行。 200ms 待機しました。
直列で実行されるPromise(async/await)
短い直列の場合、恩恵はそこまで無いのが正直な所でしょうか。
とはいえ通常(=同期処理)のように処理の呼び出しをつらつらと書けるのは可読性が高くなります。
コード
(function() { // asyncFuncは非同期処理。Promiseオブジェクトを返却する。 // 引数によってtimeoutの時間が異なる。 const asyncFunc = function(count, sleep) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log( "asyncFunc " + count + " 回目の実行。\n" + sleep + "ms 待機しました。" ); resolve(); }, sleep); }); } // 非同期処理を直列で実行する。 // timeout処理があるけど、ちゃんと上から順番に実行される? const callAsync = async function() { await asyncFunc(1, 100); await asyncFunc(2, 300); await asyncFunc(3, 200); } callAsync(); })();
実行結果
promise.js:7 asyncFunc 1 回目の実行。 100ms 待機しました。 promise.js:7 asyncFunc 2 回目の実行。 300ms 待機しました。 promise.js:7 asyncFunc 3 回目の実行。 200ms 待機しました。
直列で複数回実行されるPromise(ES5)
then()でつなげる回数が少ない場合は上記の書き方でよいですが、もっと数が多くなると大変です。
そのような場合は非同期処理を実行する関数を呼ぶ関数を作り、再帰的に呼び出します。
難しい・・・。
こんな感じです。
コード
(function() { const timers = [100, 500, 2000, 300, 200]; // asyncFuncは非同期処理。Promiseオブジェクトを返却する。 // 引数によってtimeoutの時間が異なる。 const asyncFunc = function(count, sleep) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log( "asyncFunc " + count + " 回目の実行。\n" + sleep + "ms 待機しました。" ); resolve(); }, sleep); }); } // 非同期処理を直列で実行する。 // timeout処理があるけど、ちゃんと上から順番に実行される? const callAsync = function(dataArray, _index) { let index = _index ? _index : 0; return asyncFunc(index+1, dataArray[index]) .then(function() { ++index; if (index < dataArray.length) { return callAsync(dataArray, index); } else { return; } }); } callAsync(timers); })();
実行結果
promise.js:10 asyncFunc 1 回目の実行。 100ms 待機しました。 promise.js:10 asyncFunc 2 回目の実行。 500ms 待機しました。 promise.js:10 asyncFunc 3 回目の実行。 2000ms 待機しました。 promise.js:10 asyncFunc 4 回目の実行。 300ms 待機しました。 promise.js:10 asyncFunc 5 回目の実行。 200ms 待機しました。
直列で複数回実行されるPromise(async/await)
コード
(function() { const timers = [100, 500, 2000, 300, 200]; // asyncFuncは非同期処理。Promiseオブジェクトを返却する。 // 引数によってtimeoutの時間が異なる。 const asyncFunc = function(count, sleep) { return new Promise(function(resolve, reject) { setTimeout(function() { console.log( "asyncFunc " + count + " 回目の実行。\n" + sleep + "ms 待機しました。" ); resolve(); }, sleep); }); } // 非同期処理を直列で実行する。 // timeout処理があるけど、ちゃんと上から順番に実行される? const callAsync = async function() { for (let i=0; i<timers.length; i++) { await asyncFunc(i+1, timers[i]); } } callAsync(); })();
実行結果
promise.js:10 asyncFunc 1 回目の実行。 100ms 待機しました。 promise.js:10 asyncFunc 2 回目の実行。 500ms 待機しました。 promise.js:10 asyncFunc 3 回目の実行。 2000ms 待機しました。 promise.js:10 asyncFunc 4 回目の実行。 300ms 待機しました。 promise.js:10 asyncFunc 5 回目の実行。 200ms 待機しました。
このケースの場合、async/awaitだと再帰呼び出しの必要がなくなり、for()で回すだけで良いので、結構嬉しい。
並列の場合は?
順不同で良い場合は、for()で非同期処理を実行し、返却されたPromiseを配列につめてPromise.all()すればOK。
以前記載した通りですね。