このところ、JavaScriptやTypeScriptのPromiseを猛烈に勉強中だ。
完了までに数秒掛かる非同期処理1があり、
それが成功したら
同じく数秒掛かる非同期処理2を実行したい。
それが成功したら、
同じく数秒掛かる非同期処理3を実行したい…
のような状況では、Promiseが最適だ。
例えばこんな処理があるとする。
乱数で0~9を発生して0の場合をエラー、それ以外を成功とする。
setTimeout(function () { // 完了まで1秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { alert('何かエラー発生'); } else { // 成功した場合の処理 } }, 1000);
成功した場合に、引き続き類似の処理をしたい。
実際の場面では、乱数でエラー判定するなんて言う事は無いが、何度か実行するとたまにエラーする状況を乱数でシミュレーションしている。
このような非同期処理を連続的に行う場合に、Promiseの使い方を覚えよう。
では、本題に入ろう。
従来式の入れ子作戦
もしPromiseを使わないと、入れ子作戦となる。
setTimeout(function () { // 完了まで1秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { alert('何かエラー発生'); } else { // 成功した場合の処理 setTimeout(function () { // 完了まで1秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { alert('何かエラー発生'); } else { // 成功した場合の処理 alert('無事に完了'); } }, 1000); } }, 1000);
二重の入れ子くらいならまあ問題ないが、三重、四重となると、{}の数も増えるし、訳分からなくなる。
Promiseでスッキリと簡潔に書ける
こういう場合にPromiseを使うと、こんな感じに書けるのかな。
最近覚えたばかりなのだが。
function calculate() { return new Promise(function (resolve, reject) { setTimeout(function () { // 完了まで数秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { reject( '何かエラー発生'); } else { resolve('無事に完了'); } }, 1000); }); }; function calculate2() { return new Promise(function (resolve, reject) { setTimeout(function () { // 完了まで数秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { reject( '何かエラー発生'); } else { resolve('無事に完了'); } }, 2000); }); }; calculate() .then(calculate2) .then(successFunc) .catch(errorFunc); function successFunc(result) { alert(result); }; function errorFunc(msg) { alert(msg); }
これなら、三重でも四重でも何重でも簡潔に書ける。
Promiseで四連結実行の例
四重の例を作成してみた。
各関数間で引数の受け渡しも行える。
直前の関数が成功した場合には、その戻り値を、次段の関数の引数として渡している。
冒頭のcloudflare.comから読み込んでいる理由は、IE11の場合にはこの行が無いとPromiseが動かなかったので。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title></title> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/es6-promise/3.2.1/es6-promise.js"> </script> </head> <body> <button onclick="calcStart();">計算開始</button> <h1 id="valueNow_ID"></h1> <script> function calculate(value) { document.getElementById('valueNow_ID').innerHTML = value + '回目[calculate]'; return new Promise(function (resolve, reject) { setTimeout(function () { // 完了まで数秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { reject(value + '回目の処理で何かエラー発生'); } else { resolve(value + 1); } }, 1000); }); }; function calculate2(value) { document.getElementById('valueNow_ID').innerHTML = value + '回目[calculate2]'; return new Promise(function (resolve, reject) { setTimeout(function () { // 完了まで数秒かかる重い処理。10%の確率でエラーする。 var n = parseInt(Math.random() * 10, 10); if (n === 0) { reject(value + '回目の処理で何かエラー発生'); } else { resolve(value + 1); } }, 2000); }); }; function calcStart() { calculate(1) .then(calculate2) .then(calculate) .then(calculate2) .then(successFunc) .catch(errorFunc); } function successFunc(result) { document.getElementById('valueNow_ID').innerHTML = result + '完了'; }; function errorFunc(msg) { document.getElementById('valueNow_ID').innerHTML = msg + '中断'; } </script> </body> </html>
完全なHTML版だ。
この部分をコピペしてメモ帳などに貼り付けて、適当なファイル名
test.html
などで保存(文字コードはUTF-8)。
そのhtmlファイルをブラウザで開くと実行出来る。
あるいは、下のボタンをクリックしても同じ処理が実行出来るようにしたので試して下さい。
全部成功して最後まで行く場合が多いが、何度か繰り返していると、途中でエラー中断する状況も見られる。
calculate(1);
で引数1を渡して関数calculate(value)を実行。
それが成功したら戻り値を使って
calculate2(value);
を実行。あとはこれを二回繰り返して、合計4回連続で関数を実行する。
全部成功したら
successFunc();
どこかでエラーしたら
errorFunc();
が実行される。
まとめ
ワテも覚えたばかりのプロミスなので、もし間違っていたらご指摘下さい。
Promiseは便利だ。
Promise関連
コメント