Promiseについて
Promiseとは、
- 非同期処理を制御するためのしくみ
- ES2015以降で使用可能
メリット
その1. コードが簡潔になり、見やすくなる。(個人差あり)
例えば、非同期関数func1〜func3があったとして、下記の制約がある場合、
- func2はfunc1の結果が必要
- func3はfunc2の結果が必要
一般的なコールバック関数を使用した場合、ソースは下記のようになる。
(典型的な「コールバック地獄」というやつです)
※しかもエラー処理を省略してるので、エラー処理も書いたらもっと煩雑になる...
function main() { func1((err, data) => { func2(data, (err2, data2) => { func3(data2, (err3, data3) => { console.log(data3); }); }); }); }
これがPromiseを使えば、例えばこうなります。
// resolveの引数は、正常終了した際の戻り値。 // rejectの引数は、エラー発生した際の戻り値。 function funcPromise1() { return new Promise(function(resolve, reject) { func1((err, data) => { if(!err) { resolve(data); } else { reject(err); } }); }); } function funcPromise2(data) { return new Promise(function(resolve, reject) { func2(data, (err, data2) => { if(!err) { resolve(data2); } else { reject(err); } }); }); } function funcPromise3(data2) { return new Promise(function(resolve, reject) { func3(data2, (err, data3) => { if(!err) { resolve(data3); } else { reject(err); } }); }); } function main() { funcPromise1.then((data) => {return funcPromise2(data);}) // resolveされた場合、then()の引数にはresolveの引数が格納される。 // rejectされた場合、catch()の引数にはrejectの引数が格納される。 .then((data2) => { return funcPromise3(data2); }) .then((data3) => { console.log(data3); }) .catch((err) => { console.log(err); }); }
funcPromise1~funcPromise3の関数定義は増えましたが、ソース自体は見やすくなったと思います。
エラー処理が簡潔になる
(先程のソースで若干ネタバレしてますが)func1〜func3のいずれかでエラーが発生しても、Promiseなら最後の「catch(err)」でまとめてエラー処理ができます。
これがコールバック関数の場合、こうなります。
(クッソ見にくい...というか、try~catchを使った場合、全体を囲ってtry~catchではエラー捕捉ができない(=毎回外側にthrowしなくちゃいけない)ので、もっと大変なことに。)
function main() { func1((err, data) => { if(!err) { func2(data, (err2, data2) => { if(!err2) { func3(data2, (err3, data3) => { if(!err3) { console.log(data3); } else { console.log(err3); } }); } else { console.log(err2); } }); } else { console.log(err2); } }); }
非同期処理の並行実行の制御ができる
Promiseを使った場合、例えば複数の非同期処理を同時実行した場合に、下記のような処理が可能です。
function main() { // Promise.race()は、引数の非同期処理のうち、 // いずれか1個の処理が完了したら次へ進む Promise.race([funcPromise, funcPromise2, funcPromise3]) // 最初に終わった処理がresolveの場合、thenの引数dataに // その処理のresolve値が格納される。 // また最初に終わった処理がrejectの場合、catchの引数errに // その処理のreject値が格納される。 .then((data) => { console.log(data); }) .catch((err) => { console.log(err); }); // Promise.all()は、引数の非同期処理がすべて終了するまで次へ進まない。 Promise.all([funcPromise, funcPromise2, funcPromise3]) // 引数data~data3には、それぞれfuncPromise~funcPromise3の // resolve値が格納される。(すべてresolveしないとthen()は実行されない) .then(([data, data2, data3]) => { console.log(data); console.log(data2); console.log(data3); }) // いずれか1つでもrejectされた場合、最初にrejectされた値の // reject値が格納される。 .catch((err) => { console.log(err); }); }
そして、話はasync/awaitにつながる訳ですが、それはまた後日。