2年ぐらい前にgeneratorでコールバック地獄から解放されるぜウェーイと話題になりましたが、今現在、世間ではgeneratorによる非同期処理ってどのくらい使われているんでしょうね? 自分たちのチームでは今更過去のコードを書き直すのも面倒なので使ってません。個人的にはasync/awaitがES.nextに入りそうなので今から使わなくてもいいかなぁ、という感じ。
generatorを使った書き方、よく忘れるのでここにメモしておく
まず適当なプロミスを返す関数を定義
'use strict'; // 引数を2倍して解決するpromiseを返す function twice(value) { return Promise.resolve(value * 2); } // 引数ミリ秒後に解決するpromiseを返す function sleep(time) { return new Promise((resolve) => { setTimeout(() => {resolve(time)}, time); }); } // 失敗するpromiseを返す function error(e) { return Promise.reject(new Error(e)); }
promiseをyieldして非同期処理を実行するexecAsyncを定義、おそらくcoモジュールもこんな感じの処理をしているんでしょうね。
// promiseをyieldして非同期処理を実行するexecAsyncを定義 function execAsync(generator) { const g = generator(); let n = g.next(); // ここで最初のyieldまで実行される if (!n.done) { // yieldが無かった場合はn.doneはtrueになる loop(n.value); // n.valueは最初のyield式のpromise } function loop(p) { p.then((result) => { let n = g.next(result); // nextの引数はyield式の評価結果として返される。そして次のyieldまで実行 if (!n.done) { loop(n.value); } }).catch((e) => { let n = g.throw(e); if (!n.done) { loop(n.value); } }); } }
実行する
// 実行! execAsync(function *() { let a; a = yield twice(1); console.log(a); // => 2 a = yield twice(2); console.log(a); // => 4 const t = yield sleep(1000); console.log(`${t}ミリ秒停止した`); try { yield error('Promiseがrejectされると'); } catch (e) { console.log(`${e.message}エラーがスローされる`); } a = yield twice(3); console.log(a); // => 6 a = yield twice(4); console.log(a); // => 8 });
結果
2 4 1000ミリ秒停止した Promiseがrejectされるとエラーがスローされる 6 8