JavaScriptの非同期処理を整理した(Promise、Deferred、async/await)
JavaScriptの非同期処理を整理した(Promise、Deferred、async/await)
Promise
Promiseは、JavaScriptの非同期処理機能である。
基本的な使い方(thenで受ける)
- Promiseオブジェクトを生成
- 時間のかかる処理が
- 正常に終わりresolveが呼ばれたとき→thenメソッドの第一引数の関数が実行される
- 異常終了でrejectが呼ばれたとき→thenメソッドの第二引数の関数が実行される
let p = new Promise((resolve, reject) => { //時間のかかる処理 setTimeout(() => { resolve(); }, 2000); console.log('myfunc called'); }) .then(() => { console.log('fulfilled'); }, () => { console.log('rejected'); });
基本的な使い方(thenとcatchで受ける)
thenでresolveとrejectの両方受けると読みにくいので、以下のようにthenでresolveを、catchでrejectを受けるとちょっと読みやすい。
let p = new Promise((resolve, reject) => { //時間のかかる処理 setTimeout(() => { reject(); }, 2000); console.log('myfunc called'); }) .then(() => { console.log('fulfilled'); }) .catch(() => { console.log('rejected'); });
直列実行(thenのメソッドチェーン)
thenは複数チェーンで繋げることができる。時間のかかる複数の処理を順番に実行することができる。
let p = new Promise((resolve, reject) => { //時間のかかる処理 setTimeout(() => { resolve(); }, 2000); console.log('myfunc called'); }) .then(() => { //ここに時間のかかる処理を書く console.log('fulfilled 1'); }) .then(() => { console.log('fulfilled 2'); }) .catch(() => { console.log('rejected'); });
myfunc called fulfilled 1 fulfilled 2
上記は、最初のPromiseオブジェクトをどんどん伝搬させていくので、最初の処理が問題なければ以降すべてのthenが実行される。
2つ目以降の処理で失敗したときにこのチェーンを抜ける場合は、thenメソッド内で別途Promiseオブジェクトを生成し、実行結果に応じてresolve/rejectを設定してやればよい。
並列実行(複数のPromiseを待つ)
例えば3つの処理すべてが終わるのを待つ場合、Promise.allを使う。
const promise1 = Promise.resolve(3); const promise2 = 42; const promise3 = new Promise((resolve, reject) => { setTimeout(resolve, 2000, 'foo'); }); Promise.all([promise1, promise2, promise3]).then((values) => { console.log(values); });
Array [3, 42, "foo"]
例外処理
Promiseを呼び出す部分での例外処理。
- try/catchで囲い、catchしたらrejectを呼ぶ。
return new Promise((resolve, reject) => { try { throw new Error(); resolve(); } catch(e) { reject(e); } });
Promise受ける側での例外処理(すでに上記に書いたものの再掲)
- catchメソッドを書く
.then((v) => { return v; }) .catch((e) => { throw e; });
参考URL:
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Deferred
Deferredは、jQueryにおける非同期処理の機構である。Promiseを包含している。
- Deferred()でDeferredオブジェクトを生成する
- 時間のかかる処理が
- 正常に終わったときにresolve()メソッドを呼ぶ→done()で定義した処理が実行される
- 失敗したときにreject()を呼ぶ→fail()で定義した処理が実行される
let df = $.Deferred(); df.done(() => { console.log('done'); }); df.fail(() => { console.log('fail'); }); //時間のかかる処理 setTimeout(() => { df.resolve(); }, 2000);
参考URL:
async/await
async/awaitは、JavaScriptの非同期処理機能である。
Promiseよりも簡潔に書ける一方で、一部のブラウザでは対応していない(非対応:IE11、Edge14など)。
asyncの使い方
- 関数の頭にasyncをつけるとPromiseオブジェクトを返す
- 正常終了の場合はreturnする→thenメソッドが実行される
- 異常終了の場合はErrorをthrowする→catchメソッドが実行される
正常終了の例:
async function myfunc() { return 'resolved'; } myfunc().then((v) => { console.log(v); });
異常終了の例:
async function myfunc() { throw new Error('rejected'); } myfunc().catch((e) => { console.log(e.message); });
ただ、基本的にはasyncとawaitはセットで使うため、下記async/awaitの使い方を参照するとよいです。
async/awaitの使い方
- 時間のかかる関数でPromiseオブジェクトをreturnする
- asyncのついた関数内でawaitをつけてその関数を呼ぶと、時間のかかる関数を待つ
- asyncの関数に応じてthenメソッドが呼ばれる
function myfunc() { return new Promise(resolve => { setTimeout(() => resolve('done'), 2000); }); } async function myasyncfunc() { let ret = await myfunc(); return ret; } myasyncfunc().then((v) => { console.log(v); });
直列処理
時間のかかる処理1が終わったら時間のかかる処理2を実行、それが終わったら・・・というふうに、順番に実行していくパターン。
function myfunc(v) { return new Promise(resolve => { setTimeout(() => { console.log('done' + v); resolve(); }, 2000); }); } async function myasyncfunc() { let ret1 = await myfunc('1'); let ret2 = await myfunc('2'); let ret3 = await myfunc('3'); return 'all done'; } myasyncfunc().then((v) => { console.log(v); });
(2秒待つ) done1 (2秒待つ) done2 (2秒待つ) done3 all done
並列処理
Promise.allで待つことで並列に実行し、すべての結果を待つことができる。
function myfunc(v) { return new Promise(resolve => { setTimeout(() => { console.log('done' + v); resolve(v); }, 2000); }); } async function myasyncfunc() { const [a, b, c] = await Promise.all([myfunc('1'), myfunc('2'), myfunc('3')]); return [a, b, c]; } myasyncfunc().then(([a, b, c]) => { console.log(a, b, c); });
(2秒待つ) done1 done2 done3 1 2 3
例外処理
Promise生成部分はPromiseの章に記載したのと同じ。
awaitで受ける部分の例外処理
- asyncの関数内は、myfunc呼び出しで例外が発生したらそのまま処理中断される
- そのエラーはcatchで受けられる
async function errfunc() { const ret = await myfunc(); return ret; } errfunc().catch((e) => { console.log(e); });
参考URL: