はじめに
Promiseの基本とthen()を使ったメソッドチェーンの使い方で、以下を中心にまとめています。
- 複数の非同期処理を順番に実行したい(直列)、同時に実行したい(並列)
- Promiseの直列処理で、複数の値を受け取りたい
- then()を使ったメソッドチェーンで、関数の呼び出しに引数を渡したい
これからPromiseを使いたい人の参考になれば幸いです。
Promiseとは
非同期処理の結果を、成功(resolve) または、失敗(reject)で返すオブジェクトです。
Promiseを使用すると、以下のような非同期処理を簡潔に書けます。
- 非同期処理の成功、失敗の処理を分岐する。
- 複数の非同期処理を順番に実行したり、並行して実行する。(直列・並列)
Promiseの基本
new Promise
でインスタンスを生成して使用します。
Promiseの引数には関数を指定し、その関数内に実行したい非同期処理を記述します。
var myPromise = new Promise(function (resolve, reject) {
// 実行したい処理を記述
setTimeout(function() {
// 成功
resolve('成功!'); // resolve(渡したい値)
}, 3000);
});
myPromise
.then(function(value) {
// 非同期処理が成功した場合
console.log('実行結果:' + value); // => 実行結果:成功!
})
.catch(function(value) {
// 非同期処理が失敗した場合
console.log('実行結果:' + value); // 呼ばれない
});
-
function (resolve, reject) {...}
を実行した後に、new Promise
を実行。 -
function (resolve, reject) {...}
の処理が成功の場合はresolve()
を、失敗の場合はreject()
を明示してインスタンスに渡す。 -
.then()
でresolve()
の場合、.catch()
でreject()
の場合の処理を受ける。
複数の値を受け渡す
// 複数の値を配列で渡す
var myPromise2 = new Promise(function (resolve, reject) {
// 複数の値を渡す場合は、配列にまとめる
resolve([123, 'abc']); // resolve([値1, 値2...])
});
myPromise2
.then(function(value) {
console.log(value[0]); // => 123
console.log(value[1]); // => abc
});
実行パターン
順番に実行 - その1 (直列)
Promiseはthen()
メソッドを使って、複数の処理をつなぎ、順番に実行することが出来ます。
以下はthen()で繋いだPromiseのメソッドチェーン。
// 上から順番に処理を実行する
Promise.resolve()
.then(function(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('task1 処理中...');
resolve('task1 完了!');
}, 1000);
});
})
.then(function(value){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log(value); // => task1 完了!
console.log('task2 処理中...');
resolve(['task2 完了!', 123]); // 複数の値を渡す時は、配列にまとめる
}, 3000);
});
})
.then(function(value){ // resolveで渡された値の受け取り => ['task2 完了!', 123]
return new Promise(function (resolve, reject) {
console.log(value[0]); // => task2 完了!
console.log(value[1]); // => 123
console.log('task3 完了!');
resolve();
});
});
-
resolve()
で渡した値は、次の.then(function(value){...})
内のvalueで受け取ります。
setTimeoutで時間差をつけても、以下のように順番に実行されます。
task1 処理中...
task1 完了!
task2 処理中...
task2 完了!
123
task3 完了!
順番に実行 - その2 (直列)
直列処理は以下のように、then()と関数を分けて書くこともできます。
(実行結果は「複数の非同期処理を順番に実行 - その1 (直列)」と同じ)
引数を指定しない場合then(実行したい関数名)
で()は不要です。
// 上から順番に処理を実行する
Promise.resolve()
.then(task1) // .then(実行したい関数名)
.then(task2)
.then(function(value){ // 直に処理を直接書いてもOK
return new Promise(function (resolve, reject) {
console.log(value[0]);
console.log(value[1]);
console.log('task3 完了!');
resolve();
});
});
function task1(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('task1 処理中...');
resolve('task1 完了!');
}, 1000);
});
}
function task2(value){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log(value);
console.log('task2 処理中...');
resolve(['task2 完了!', 123]); // 複数の値を渡す時は、配列にまとめる
}, 3000);
});
}
then()で呼び出す関数に引数を渡す
bind()
を使えば、then()
で実行する関数に引数を渡せます。
Promise.resolve()
.then(task1.bind(null, '123'))
.then(task2.bind(null, ['aaa', 'bbb'])) // bindでtask2の第1引数を固定
.then(function(value){
return new Promise(function (resolve, reject) {
console.log(value);
console.log('task3 完了!');
resolve();
});
})
function task1(msg){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('task1 処理中...');
console.log(msg); // => 123
resolve(['task1 完了!', msg]);
}, 1000);
});
}
// thenでtask2を呼び出す時に、第一引数をbindで固定しているため、値の受け取りは以下になる
// 第1引数 args => ['aaa','bbb']
// 第2引数 value => ['task1 完了!', '123']
function task2(args, value){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log(value[0]); // => task1 完了!
console.log(value[1]); // => 123
console.log(args[0]); // => aaa
console.log(args[1]); // => bbb
console.log('task2 処理中...');
resolve('task2 完了!');
}, 3000);
});
}
同時に実行 - その1 (並列)
並列処理は、Promise.all()
を使います。
Promise.all([
taskA(), //同時に実行したい関数名
taskB(),
taskC()
]);
function taskA(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('taskA 完了!');
resolve();
}, 1000);
});
}
function taskB(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('taskB 完了!');
resolve();
}, 3000);
});
}
function taskC(){
return new Promise(function (resolve, reject) {
console.log('taskC 完了!');
resolve();
});
}
taskC 完了!
taskB 完了!
taskA 完了!
同時に実行 - その2 (並列)
並列処理は以下のように、関数にまとめることもできます。
(実行結果は「同時に実行 その2 (並列)」と同じ)
Promise.resolve()
.then(parallel) // 関数にまとめた並列処理を実行
function parallel() {
return Promise.all([
taskA(), //同時に実行したい関数名
taskB(),
taskC()
]);
}
function taskA(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('taskA 完了!');
resolve();
}, 1000);
});
}
function taskB(){
return new Promise(function (resolve, reject) {
setTimeout(function() {
console.log('taskB 完了!');
resolve();
}, 3000);
});
}
function taskC(){
return new Promise(function (resolve, reject) {
console.log('taskC 完了!');
resolve();
});
}
IE11でPromiseを使う
IE11はPromiseに対応していません。
Promiseを使いたい場合は、es6-promiseなどのpolyfillを使います。
おわりに
説明を省略している部分もありますが、実際にPromiseを使う上で必要になりそうなことは、まとめることが出来たと思います。
なお、iOS9はES6に対応していないので、ネイティブのPromise実装はできません。
各ブラウザの対応状況は、こちらで確認できます。