Promiseを使って同期的に処理する
Promiseを使って複数の非同期処理を順番に制御してみましょう。
function func1(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num);
},1000);
});
}
function func2(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num*num);
},1000);
});
}
func1(2)
.then(func2)
.then(function(result) {
console.log(result);
})
.catch(e) {
console.error(e);
});
func1の処理の完了を待ち、処理結果をfunc2で受けてデータを返すという形で順番に処理が実行されます。コードとしても順番に処理されているのがわかりやすくなっています。
これを従来のコードで書いた場合には、下記のようになります。
function func1(num,callback,onFailed) {
window.setTimeout(function() {
callback(num);
},1000);
}
function func2(num,callback,onFailed) {
window.setTimeout(function() {
callback(num*num);
},1000);
}
func1(2, function(num) {
func2(num, function(result) {
console.log(result);
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
このようにするとfuncがどんどん入れ子になってインデントが深くなっていきます。エラーの処理もしにくくなり読みにくくなっていますね。まだ2つだから良いですが、これが5つ6つとなっていくとメンテナンス性はさらに悪くなっていきます。
func1(2, function(num) {
func2(num, function(num) {
func3(num, function(num) {
func4(num, function(num) {
func5(num, function(num) {
console.log(num);
});
});
});
});
});
F
これをPromiseを用いた場合は、下記のようにすっきりとさせることが可能です。
func1(2)
.then(func2)
.then(func3)
.then(func4)
.then(func5)
.then(function(num) {
console.log(num);
});
どちらが処理を追いやすいかは一目瞭然ですね。
Promiseを使って並列処理して完了を検知する
続いて、複数の非同期処理を並列で実行し、すべてが完了したら情報を取得する処理をしたいと思います。これが非同期のメリットですものね。
function func1(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num+num);
},500);
});
}
function func2(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num*num);
},2000);
});
}
Promise.all([func1(3),func2(3)]).then(function(results) {
console.log(results);
}).catch(function(e) {
console.log(e);
});
Promise.all([Promise,Promise,...])
を使って非同期な処理を並列でおこない、すべてがresolveになるタイミングをthenで取得します。
並列で処理しているものが1つでもrejectとなった場合はcatchが実行されます。
function func1(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num+num);
},500);
});
}
function func2(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
reject(num*num);
},2000);
});
}
Promise.all([func1(3),func2(3)]).then(function(results) {
console.log(results);
}).catch(function(e) {
console.log(e);
});
従来は完了数をカウントして全部終わったかどうかを確認するなどしていましたが、とても簡単に並列処理の完了を捕捉することができます。
1個でも処理が返ってきたら実行する
続いて少し用途は狭くなりますが、並列で処理しているものが1つでもresolveになったら実行する処理を作りたいと思います。
function func1(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(num+num);
},500);
});
}
function func2(num) {
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
reject(num*num);
},2000);
});
}
Promise.race([func1(3),func2(3)]).then(function(results) {
console.log(results);
}).catch(function(e) {
console.log(e);
});
Promise.race([Promise,Promise,...])
を使うことで並列で実行した処理でもっとも早く結果を返してきたらthenやcatchが実行されるようになります。もし最初に結果を返してきたものがrejectであれば、catchが実行されます。
使いどころはやや難しいですね。
まとめ
このようにPromiseを使うと非同期の処理をとても制御しやすくなります。とくに、Node.jsでは非同期処理が多いので積極的にPromiseを活用して制御していきましょう。
なお、本稿ではPromiseesというPromiseの挙動を可視化するツールを用いて紹介しました。Promiseの処理が複雑になってしまったときの活用や動作フローの確認などに使えるので、積極的に活用してみてください。
- Promisees · Courtesy of ponyfoo.com:
http://bevacqua.github.io/promisees/#
それでは、また。
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。