Node.jsエンジニアなら2014年内に知っておきたいPromise入門
Promiseは非同期処理をベースにした並列処理の実装方法の一種です。Promiseでは並列処理の各タスクが必ず1回実行されることが保証され、タスクとタスクからの結果を取得する処理を分離することできます。また、タスクの並列処理/直列処理をユーザーが自由に制御することができます。
ECMAScript Language Specification 6th EditionにPromiseが追加されたことから、しばらくの間JavaScriptの非同期処理に関する話題の中心はPromiseになると思われます。
Node.jsでも既にPromise対応したモジュールも多く、Promiseでのみ非同期処理を提供しているモジュールも存在します。Promiseを使用する機会は今後必然的に増えていくでしょう。
今回のエントリーでは、Node.jsでPromiseを使用する方法を説明します。コード例を中心に理論的な細部にはあまり立ち入らないよう構成していますので、はじめてPromiseに触れる方も簡単に理解できる内容になっています。加えて、ニーズの高いと思われるasync.jsとPromiseの変換方法も紹介しています。
ぜひこの機会にPromiseをプログラミングスキルに追加してください。
1. Promiseモジュールのインストール
現在開発中のNode.js v0.12では--harmony
オプションを指定して実行することによりネイティブのPromise実装を使用することができます。しかし、v0.12はまだmasterブランチ上にしか存在せず、現時点の安定版であるV0.10系列のNode.jsではPromiseを実装したモジュールを別途インストールすることにより使用できるようになります。
npmパッケージとして使用可能なPromiseモジュールには以下のものがあります。
今回はこれらの中からbluebirdモジュールを使用することにします。
bluebirdはECMAScriptの仕様に準拠したインタフェースを持っており、Promise非対応メソッドをPromise対応させるpromisifyAll
という大変便利な機能も実装されています。他のモジュールも基本的な使い方は変わりませんので、bluebirdでPromiseの使い方を覚えたら色々と試してみるのも良いかと思います。
1-1. bluebirdモジュールのインストール方法
npmコマンドを実行してモジュールをインストールします。
$ npm install bluebird npm http GET https://registry.npmjs.org/bluebird npm http 304 https://registry.npmjs.org/bluebird bluebird@2.3.2 node_modules/bluebird
実行するとnode_modules/bluebirdディレクトリにモジュールがインストールされます。スクリプトを作成する際は、npmコマンドを実行したディレクトリ(node_modeulsディレクトリの親ディレクトリ)に置いてください。
2. First promise
bluebirdモジュールのインストールが終わったら、Promiseを使った最初のスクリプトを書いてみましょう。以下のスクリプトをfirst_promise.js
として保存してください。
// first_promise.js var fs = require('fs'); var Promise = require('bluebird'); Promise.promisifyAll(fs); fs.readFileAsync('first_promise.js', 'utf-8').then(function(value) { console.log(value); }, function(error) { console.error(error); });
このスクリプトは自分自身(first_promise.js
)を読み込んでそのまま出力します。スクリプトを実行すると以下の出力が得られます。
$ node ./first_promise.js // first_promise.js var fs = require('fs'); var Promise = require('bluebird'); Promise.promisifyAll(fs); fs.readFileAsync('first_promise.js', 'utf-8').then(function(value) { console.log(value); }, function(error) { console.error(error); }); $
first_promise.jsでは以下の処理を実行しています。
- fsモジュールとbluebirdモジュールをrequireで読み込みます。
Promise.promisifyAll
を実行し、fsモジュールのすべてのメソッドをPromise対応させます。fs.readFileAsync
を実行します。(readFileAsync
メソッドはPromise.promisifyAll
がfsモジュールに追加した、Promise対応版のreadFile
メソッドです。Promise.promisifyAll
は*Async
という名前でPromise対応版メソッドをモジュールに追加します。)readFileAsync
が返すPromiseオブジェクトの.then
メソッドを使用し非同期処理終了後に実行されるコールバック処理を登録します。
このようにPromiseでは.then
メソッドに非同期処理が完了した後に実行すべき処理を記述します。
fs.readFileAsync
関数はPromiseオブジェクトを返し、.then
はそのPromiseオブジェクトのメソッドです。
2-1. .then
.then
メソッドは、非同期処理に成功した場合に呼び出されるonFulfilled
と、失敗した場合に呼び出されるonRejected
の2つの関数を引数を受け取ります。
promise.then(onFulfilled, onRejected)
onFulfilled
コールバック関数は非同期処理が成功した場合に、非同期処理から返された値を引数に実行されます。
onFulfilled(value)
onRejected
コールバック関数は非同期処理が失敗した場合に、非同期処理から返されたエラーを引数に実行されます。
onRejected(error)
onFulfilled
、onRejected
ともにオプション引数で省略可能です。関数以外が渡された場合は無効にされます。
3. .then
メソッドチェイン
.then
メソッドはメソッドチェインとして使用されることを前提に設計されています。
以下のスクリプトsecond_promise.js
は、先ほどのfirst_promise.js
を少し改造して.then
メソッドをチェインさせたものです。
// second_promise.js var fs = require('fs'); var Promise = require('bluebird'); Promise.promisifyAll(fs); fs.readFileAsync('second_promise.js', 'utf-8').then(function(value) { console.log(value); return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled('setTimeout completed'); //onRejected(new Error('setTimeout failed')); }, 1000); }); }).then(function(value) { console.log(value); }, function(error) { console.error(error); });
second_promise.js
では以下の処理を行っています。
fs.readFileAsync
でsecond_promise.js
を読み込み出力する部分まではfirst_promise.js
と同じです。fs.readFileAsync.then
メソッドのコールバックではnew Promise
で非同期処理を実行するPromise オブジェクトを返します。Promiseオブジェクトのコンストラクタに渡される関数はリゾルバ関数と呼ばれ、Promiseオブジェクトが返された次の.then
のonFulfilled
とonRejected
を引数に実行されます。- リゾルバ関数は
setTimeout
で1秒待ったのち、文字列を引数にonFulfilled
を呼び出し非同期処理の成功を通知します。 onFulfilled
は渡された値setTimeout completed
をconsole.log
で出力します。
3-1. リゾルバ関数
Promiseを使って実行したい非同期処理は、 Promiseオブジェクトのコンストラクタに引数として渡されるリゾルバ関数に記述します。
リゾルバ関数はonFulfilled
とonRejected
の2つの関数を引数として受け取ります。
function(onFulfilled(value), onRejected(error))
3-2. onFulfilled
onFulfilled
引数は非同期処理が成功した際に呼び出す関数です。onFulfilled
を呼び出すと次の.then
に成功が通知されます。
onFulfilled
のvalue
引数には任意の値を渡します。value
は省略可能です。
onFulfilled
に渡したvalue
は、.then
メソッドの引数として渡されるonFulfilled
コールバック関数呼び出しの際に引数として渡されます。
3-3. onRejected
onRejected
引数は非同期処理が失敗した際に呼び出す関数です。onRejected
を呼び出すと次の.then
に失敗が通知されます。
onRejected
のerror
引数には任意の値を渡します。error
は省略可能です。通常error
にはError
オブジェクトを渡します。
onRejected
に渡したerror
は、.then
メソッドの引数として渡されるonRejected
コールバック関数呼び出しの際に引数として渡されます。
以下は上記のsecond_promise.js
のsetTimeout
コールバック関数を書き換えてエラーを通知するようにした例です。
fs.readFileAsync('second_promise.js', 'utf-8').then(function(value) { console.log(value); return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { //onFulfilled('setTimeout completed'); onRejected(new Error('setTimeout failed')); }, 1000); }); }).then(function(value) { console.log(value); }, function(error) { console.error(error); });
onRejected
によりエラーが通知されると、onRejected
が無効にされている.then
をスキップし、最初に現れたonRejected
が呼び出されます。例えば以下のようにonFulfilled
しか持たない.then
を追加した場合も、エラーが通知された場合は最初のonRejected
が呼び出されます。
onRejected(new Error('setTimeout failed')); }, 1000); }); }).then(function(value) { // 足した console.log(value); }).then(function(value) { console.log(value); }, function(error) { console.error(error); });
また、リゾルバ関数中で例外が送出された際は、例外オブジェクトを引数にonRejected
が呼び出されたかのように振る舞います。setTimeout
のコールバック関数を以下のように書き換えても上の例と同じ挙動になりますが、エラーが明示的ならonRejected
を呼び出して通知した方が良いでしょう。
setTimeout(function() { //onFulfilled('setTimeout completed'); //onRejected(new Error('setTimeout failed')); throw new Error('setTimeout failed'); }, 1000);
4. .then
メソッド呼び出しの省略
以上がPromiseの基本的な使い方です。ここまでをまとめると以下のようになります。
- Promiseオブジェクトのコンストラクタに渡すリゾルバ関数で非同期処理を記述し
onFulfilled
で成功を、onRejected
で失敗を通知します。 - Promiseオブジェクトから値を取得する際は
.then
メソッドを呼び出します。成功した際の値の取得はonFulfilled
で、失敗した際のエラーの取得はonRejected
で取得します。
Promiseオブジェクトの.then
メソッドの呼び出しを省略した場合、単に非同期処理が実行されます。
var Promise = require('bluebird'); new Promise(function(onFulfilled, onRejected) { setTimeout(function() { console.log('setTimeout finish'); onFulfilled(1); }, 1000); });
このスクリプトを実行すると、1秒待ってからsetTimeout finish
と表示されます。
リゾルバ関数はPromiseのコンストラクタ中で即実行され、.then
メソッドの呼び出しをもって実行される訳では無い点に留意してください。また、同じ理由から、.then
のonFulfilled
とonRejected
がそのままリゾルバ関数に渡される訳ではありません。(そのまま渡すような実装にしてしまうと、失敗した際に初めて現れたonRejected
にエラーを渡すという処理が実装できません。)
5. Promiseオブジェクトのその他のメソッド
Promiseオブジェクトには.then
メソッドの他に、インスタンスメソッドとして.catch
が、クラスメソッドとして Promise.resolve
、 Promise.reject
、 Promise.all
、 Promise.race
があります。
5-1. .catch
メソッド
.catch
メソッドは、.then(undefined, function(error))
のエイリアスで、エラー通知のみ受け取ります。
var Promise = require('bluebird'); new Promise(function(onFulfilled, onRejected) { onRejected(new Error("ERROR")); }).then(function(value) { console.log(value); }).catch(function(error) { console.error(error); });
new Promise
のリゾルバ関数でエラーが通知されると、次の.then
にはonRejected
が存在しないためそのまま次の.catch
へ制御が移り、.catch
でErrorオブジェクトがconsole.error
により出力されます。(Promiseではエラーが通知されると、メソッドチェインされた.then
にonRejected
が表れるまでonFulfilled
はスキップされますので、上の例のconsole.log(value);
は実行されません。)
5-2. Promise.resolve
クラスメソッド
Promise.resolve
クラスメソッドは引数で渡された値で成功するPromiseオブジェクトを返します。
var Promise = require('bluebird'); Promise.resolve("success").then(function(value) { console.log(value); });
このコードを実行するとsuccess
と出力されます。
メソッドチェインの先頭のダミーオブジェクトに置きたい場合、Promise.resolve
クラスメソッドがよく使用されます。
5-3. Promise.reject
クラスメソッド
Promise.reject
クラスメソッドは引数で渡された値で失敗する(エラー通知する)Promiseオブジェクトを返します。
var Promise = require('bluebird'); Promise.reject("failed").then(function(value) { console.log(value); }).catch(function(error) { console.error(error); });
このコードを実行するとfailed
と出力されます。
5-4. Promise.all
クラスメソッド
Promise.all
クラスメソッドはPromiseオブジェクトを要素とするIterableオブジェクト(JavaScript では配列)を引数に受け取り、すべての非同期処理が成功したときに成功するPromiseオブジェクトを返します。Promise.all
は非同期処理が1つでも失敗すると即失敗します。
var Promise = require('bluebird'); var p1 = new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(1); }, 1000); }); var p2 = new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(2); }, 2000); }); Promise.all([p1, p2]).then(function(value) { console.log(value); });
このコードを実行すると2秒後に[ 1, 2 ]
が出力されます。p1
とp2
は並列実行されます。
5-5. Promise.race
クラスメソッド
Promise.race
クラスメソッドはPromiseオブジェクトを要素とするIterableオブジェクト(JavaScript では配列)を引数に受け取り、最初に成功または失敗したPromiseオブジェクトを返します。
var Promise = require('bluebird'); var p1 = new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(1); }, 1000); }); var p2 = new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(2); }, 2000); }); Promise.race([p1, p2]).then(function(value) { console.log(value); });
このコードを実行すると1秒後に1
が出力されます。Promise.all
と同じくp1
とp2
は並列実行されます。
6. Promiseとasync.jsの変換
Node.jsで非同期処理を実装する場合(単純なコールバックを除いて)ポピュラーな手法は async.js でしょう。
非同期処理にasync.jsを使っているが、使用したいモジュールがPromiseしか返さないのでasync.jsで書かれたコードをPromiseに置き換えたいといったシーンもあると思います。
ここからPromiseによる非同期処理とasync.jsによる非同期処理を比較し、async.jsをPromiseに変換する方法を検討していきます。
6-1. .then
メソッドチェインとasync.waterfall
.then
メソッドチェインでは非同期処理を順次実行し、成功した場合次の.then
に値を渡します。async.jsでこの挙動と似た処理を行うのはasync.waterfall
です。async.series
も似た様な挙動ですが、非同期処理の値を次の非同期処理に渡すという意味でasync.waterfall
の方がより近い処理と言えます。
以下の2つのコードは、同じ処理をasync.waterfall
と.then
メソッドチェインで書いたものです。どちらも実行すると2
を出力します。
// async.waterfall 版 var async = require('async'); async.waterfall([ function(callback) { setTimeout(function() { callback(null, 1); }, 1000); }, function(value, callback) { setTimeout(function() { callback(null, 2); }, 1000); }, ], function(error, value) { if (error) { console.error(error); return; } console.log(value); });
// .then メソッドチェイン版 var Promise = require('bluebird'); new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(1); }, 1000); }).then(function(value) { return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(2); }, 1000); }); }).then(function(value) { console.log(value); }).catch(function(error) { console.error(error); });
async.waterfall
を.then
メソッドチェインに置き換える場合、async.waterfall
ではエラーが返されると以降の処理はすべてスキップされコールバックが呼び出されるのに対して、.then
メソッドチェインではエラーが通知されると次のonRejected
が実行されるまでonFulfilled
がスキップされる(.then
の評価自体はスキップされない)点に注意してください。
具体的には、async.waterfall
版のコードからエラーを発生させるようにするとcallback(null, 2);
を含む2つ目の処理は実行されませんが、
var async = require('async'); async.waterfall([ function(callback) { setTimeout(function() { callback(new Error('error')); }, 1000); }, // 次の関数は実行されない function(value, callback) { setTimeout(function() { callback(null, 2); }, 1000); }, ], function(error, value) { if (error) { console.error(error); return; } console.log(value); });
.then
メソッドチェイン版のコードからエラーを発生させるようにするとエラー後の.then
のonFulfilled
はonRejected
が現れるまでスキップされます。実行される処理は async.waterfall
版と同じですが、コードの意味は異なります。
var Promise = require('bluebird'); new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onRejected(new Error('error')); }, 1000); }) // この .then のリゾルバ関数が返す Promise オブジェクトは // onFulfilled しか持たないので onRejected は呼ばれず次の.thenへ制御が移る .then(function(value) { return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(2); }, 1000); }); }) // この .then のリゾルバ関数は値を返すので(return を省略すると undefined が返る) // onRejected は呼ばれず次の.thenへ制御が移る .then(function(value) { console.log(value); // .catch で onRejected をトラップ }).catch(function(error) { console.error(error); });
6-2. Promise.all
クラスメソッドとasync.parallel
Promise.all
クラスメソッドはIterableに含まれるすべてのPromiseを実行しすべて成功すると成功するPromiseを返します。async.jsでこの挙動と似た処理を行うのはasync.parallel
です。
以下の2つのコードは、同じ処理をasync.parallel
とPromise.all
で書いたものです。どちらも実行すると[ 1, 2 ]
を出力します。
var async = require('async'); async.parallel([ function(callback) { setTimeout(function() { callback(null, 1); }, 1000); }, function(callback) { setTimeout(function() { callback(null, 2); }, 1000); }, ], function(error, value) { if (error) { console.error(error); return; } console.log(value); });
var Promise = require('bluebird'); Promise.all([ new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(1); }, 1000); }), new Promise(function(onFulfilled, onRejected) { setTimeout(function() { onFulfilled(2); }, 1000); }) ]).then(function(value) { console.log(value); }).catch(function(error) { console.error(error); });
6-3. Promise.race
クラスメソッドと対応するメソッドはasync.jsにない
Promise.race
クラスメソッドはIterableオブジェクトを引数に受け取り最初に完了したPromiseオブジェクトを返します。厳密にこの挙動に対応するメソッドはasync.jsにはありません。
async.detect
やasync.parallel
などを使って似た様なコードを書く事もできますが、あまり分かりやすいコードではないので省略します。
6-4. async.each
async.eachSeries
の代替コードをPromiseで実装する
async.jsには多くのメソッドがあり、Promiseに存在していない機能も多数あります。その中でもasync.each
とasync.eachSeries
は利用頻度が高いと思われますので、Promiseでの代替コードを例示します。
6-5. async.each
をPromise.all
で置き換える
async.each
は、以下のプロトタイプを持ちます。
each(arr, iterator, callback)
配列arr
のすべての要素に関数iterator
を適用し並列実行します。すべてのiterator
の処理が終わるとcallback
が実行されます。
var async = require('async'); async.each([1, 2, 3, 4, 5], function(num, callback) { setTimeout(function() { console.log(num); callback(); }, 1000); }, function(err) { console.log('Finish'); });
上のコードは1
から5
の数字を1秒待ってから出力します。iterator
は並列起動されますので、数字の出力されるタイミングはほぼ同じになります。
これと同じ処理をPromise.all
で書くと以下のようになります。
var Promise = require('bluebird'); Promise.all([1, 2, 3, 4, 5].map(function(num) { return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { console.log(num); onFulfilled(num); }, 1000); }); })).then(function(value) { console.log('Finish'); });
数値の配列をPromiseオブジェクトの配列へと変換し、Promise.all
に渡すとasync.each
と同様の処理となります。
なお、Promise.all
は.then
のonFullfielled
に渡された値の配列を返しますので、async.map
を使った以下のコードも上のコードで代替できます。
var async = require('async'); async.map([1, 2, 3, 4, 5], function(num, callback) { setTimeout(function() {s console.log(num); callback(null, num); }, 1000); }, function(err, results) { console.log('Finish'); });
6-6. async.eachEach
をPromise.all
で置き換える
async.eachSeries
は、逐次処理版のasync.each
で、each
と同じプロトタイプを持ちます。
eachSeries(arr, iterator, callback)
配列arr
のすべての要素に関数iterator
を逐次適用し実行します。すべてのiterator
の処理が終わるとcallback
が実行されます。
var async = require('async'); async.eachSeries([1, 2, 3, 4, 5], function(num, callback) { setTimeout(function() {s console.log(num); callback(); }, 1000); }, function(err) { console.log('Finish'); });
上のコードは1秒待ってから1
から5
の数字を出力していきます。
これと同じ処理をPromiseで書くと以下のようになります。
var Promise = require('bluebird'); [1, 2, 3, 4, 5].reduce(function(promise, num) { return promise.then(function(value) { return new Promise(function(onFulfilled, onRejected) { setTimeout(function() { console.log(num); onFulfilled(num); }, 1000); }); }); }, Promise.resolve()).then(function(value) { console.log('Finish'); });
Promiseでの逐次実行はメソッドチェインになりますので、Array.reduce
でメソッドチェインを作成する処理がasync.eachEach
と同じ処理になります。Array.reduce
の初期値にPromise.resolve
の返す値を渡すことで、Array.reduce
コールバック内で配列の最初の要素か判定する処理を省略しています。
なお、メソッドチェインは最終的に.then
のonFullfielled
に渡された値の配列を返しますので、async.mapSeries
を使った以下のコードも上のコードで代替できます。
var async = require('async'); async.mapSeries([1, 2, 3, 4, 5], function(num, callback) { setTimeout(function() {s console.log(num); callback(null, num); }, 1000); }, function(err, results) { console.log('Finish'); });
6-7. Promise.promisifyAll
を使用してasync.jsをPromiseに変換する
Promiseの実装としてbluebirdモジュールを使用している場合、Promise.promisifyAll
を使用して、Promise版のasync.jsメソッドを使用することができます。
Promise.promisifyAll
はAsync
サフィックスを持つメソッドをPromise版メソッドとして追加します。上に例示したasync.each
のコードでPromiseを使用する場合はasync.eachAsync
を呼び出します。
var Promise = require('bluebird'); var async = require('async'); Promise.promisifyAll(async); async.eachAsync([1, 2, 3, 4, 5], function(num, callback) { setTimeout(function() { console.log(num); callback(); }, 1000); }).then(function(value) { console.log('Finish'); });
7. リゾルバ関数、onFulfilled
、onRejected
の返す値について
最後に、リゾルバ関数やonFulfilled
、onRejected
からPromiseオブジェクト以外の値を返した場合について説明します。
7-1. リゾルバ関数等が値を返す場合
リゾルバ関数やonFulfilled
からは以下のように値を返すこともできます。
var Promise = require('bluebird'); Promise.resolve(1).then(function() { return 2; }).then(function(value) { console.log(value); }).then(function(value) { console.log(value); });
上のコードを実行すると2
とundefined
が出力されます。2
はPromise.resolve
の.then
のリゾルバ関数から返された値、undefined
はその次の.then
のリゾルバ関数から返された値です(JavaScriptは戻り値を省略するとundefined
を返します)。
要は上のコードは以下のコードと等価ということになります。
var Promise = require('bluebird'); Promise.resolve(1).then(function() { return Promise.resolve(2); }).then(function(value) { console.log(value); return Promise.resolve(undefined); }).then(function(value) { console.log(value); });
リゾルバ関数等から値を返す、ということはPromiseが非同期実行されないことを意味します。Promiseは並列処理に関する仕組みですので、同期実行されるリゾルバ関数はあまり重要ではありませんが、return
文が無いonFulfilled
やonRejected
は頻繁に出てきます。その際はundefined
が次のonFulfilled
に渡されると覚えておくと混乱することが少ないと思います。
7-2. リゾルバ関数等がthenable
オブジェクトを返す場合
thenable
とは.then
メソッドを持つ任意のオブジェクトです。
var thenable = { then: function(onFulfilled, onRejected) { } };
リゾルバ関数やonFulfilled
からthenable
オブジェクトを返すと、Promiseはthenable
オブジェクトをPromiseオブジェクトであるかのように扱います。
var Promise = require('bluebird'); Promise.resolve().then(function() { return { then: function(onFulfilled, onRejected) { onFulfilled(1); } } }).then(function(value) { console.log(value); });
thenable
は特定のPromiseモジュールやPromise以外のthenable
を扱うことができる非同期モジュールに依存しない形で非同期処理を実装したい場合に使用できます。特定のPromiseモジュールを使用する場合は、thenable
を意識する必要は特にありません。
使用したいモジュール等がthenable
を返す実装になっておりPromiseで処理したい場合は、Promise.resolve
を実行するとPromiseオブジェクトに変換できます。もしくは.then
のonFulfilled
からthenable
オブジェクトを返しても同じように処理されます。
var Promise = require('bluebird'); function thenable_return_func() { return { then: function(onFulfilled, onRejected) { console.log('thenable'); setTimeout(function() { console.log('finish'); onFulfilled(1); }, 1000); } }; } // Promise.resolve には thenable を渡す Promise.resolve(thenable_return_func()) // .then onFulfilled に thenable を返す関数を渡す .then(thenable_return_func);
8. Promise関連リンク
最後にPromise関連資料へのリンクを掲載します。Promiseの詳細や理論的な部分に興味のある方はこちらをご覧ください。
- Promises/A+: Promise/A+の仕様書(英語)
- ECMAScript Language Specification ECMA-262 6th Edition DRAFT 25.4 Promise Objects: ECMAScript第6版のPromiseに関する章(英語)
- Promise - JavaScript | MDN: Mozilla Developer NetworkのPromiseセクション
- JavaScript Promises: There and back again - HTML5 Rocks: クライアントサイドでのPromiseの使い方の説明
- JavaScript Promiseの本: Promiseに関する包括的な説明が記載されている本
- FutureとPromise - Scala Documentation: ScalaのFuture/Promiseのマニュアル
Tokyo Otaku Mode からのお知らせ
Tokyo Otaku Modeでは非同期処理を極めたいNode.jsエンジニアを募集しています。
興味のある方はこちらからご応募ください。