Promiseで、終了後に発火させる
同期は追い越さない、非同期は追い越す
同期処理は、「追い越しが掛からない」
console.log("ステップ1");
alert("ステップ2");
console.log("ステップ3");
ステップ1 → ステップ2(OKがクリックされるまで待って)→ ステップ3
非同期処理は、「追い越しが掛かる」
console.log("ステップ1");
setTimeout( function() { alert("ステップ2");}, 3000); // 3秒後にダイアログ
console.log("ステップ3");
ステップ1 → ステップ3 → ステップ2
JavaScriptの通信は非同期処理
var xhr = new XMLHttpRequest();
xhr.open('GET','
https://somewhere');
xhr.send();
xhr.addEventListener('load', function(result){console.log("ステップ1");});
console.log("ステップ2");
ステップ2 → ステップ1 に多分なる。通信のセットや通信時間で追い越しが掛かる。そのため、ステップ1の通信結果を使って、ステップ2の処理をしようとすると、まだ値がセットされておらず、エラーになる。
1行メッセージを出すくらいなら、function(result){} の中に記述してしまえばよいが(ネスト、入子)、
- ステップ1の結果を使って、ステップ2の通信を制御
- ステップ2の結果を使って、ステップ3の通信を制御
- ステップ3の結果を使って、ステップ4の通信を制御
- (以下繰り返し)
となると、どんどんネストが深くなる。
Promise処理
- Promiseオブジェクト返す関数をつくる。
- この関数の中で、処理が終わったらresolve(value)を実行。(valueは戻り値、としておく)
- Promise.then(...)を実行すると、thenの中は、resolveが実行されるまで止まっている。
こんなプログラムを作ってみる。
function testAjax1() {
console.log("Start1");
var func1 = function() {
console.log("step1");
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log("step1 timeout");
resolve("step1 value");
}, 3000);
});
}
alert("alert stop");
var func2 = function(arg) {
console.log("step2");
console.log(arg);
}
func1().then(func2);
console.log("End1");
}
実行すると
- Start1
- (func1関数(変数)を宣言しただけでは実行はされない)
- (alertで一旦止まる → OK で再開)
- step1(func1()で実行される)
- End1(func1のsetTimeoutが効いているので、先にこっちが表示される)
- step1 timeout(setTimeoutが切れて、表示
- step2(func1のresolveが返って、func2が実行される)
- step1 value(func2が、func1のresolveの戻り値を受け取っている)
func1()のあとに出てくる End1 は、setTimeout を追い越して処理されていることに注意。
Promiseの数珠つなぎ
Promiseの処理を、数珠つないでみる。
変更点は
- func2内にsetTimeoutの処理を追加
- func3を追加
- func1().then(func2).then(func3)に変更
function testAjax1() {
console.log("Start1");
var func1 = function() {
console.log("step1");
return new Promise(function(resolve, reject){
setTimeout(function() {
console.log("step1 timeout");
resolve("step1 value");
}, 3000);
});
}
alert("alert stop");
var func2 = function(arg) {
console.log("step2");
console.log(arg);
}
var func2 = function(arg) {
console.log("step2");
console.log(arg);
setTimeout(function() {
console.log("step2 timeout");
}, 3000);
};
var func3 = function(arg) {
console.log("step3")
console.log(arg);
}
func1().then(func2).then(func3);
console.log("End1");
}
実行すると
- (途中まで略)
- step2
- step1 value
- step3
- undefined
- step2 timeout
つまり、func2のsetTimeout処理(の終了)を待たずに、func3を実行している。そこで、さらに変更を加える。
変更点は
var func2 = function(arg) {
return new Promise(function(resolve, reject){
console.log("step2");
console.log(arg);
setTimeout(function(){
console.log("step2 timeout");
resolve("step2 value");
},3000);
});
}
実行すると
- (途中まで略)
- step2
- step1 value
- step2 timeout
- step3
- step1 value
もともとfunc1のPromiseから始まった処理だったが、func2のPromise処理も数珠つなぎにできた。
for 文の中でPromiseを回す(直列実行)
// 空の解決済みPromiseを生成 <- 最初のプロミスはresolve
let promise = Promise.resolve();
for(const id of dataIds) {
// promiseにthenで繋ぎ再代入
// これでどんどんthenをチェーンしていける
// ちなみに funcPromise は Promise を返す関数
promise = promise.then(() => funcPromise(id));
}
1回目は resolve で入って、 funcPromise を実行する。
Promise オブジェクトが帰るので、2回目の実行は待ち状態になっている
for 文の中でPromiseを回す(並列実行)
配列の複数の要素に実行したときに、全部の処理が終わったことを確認して、「まとめの処理」を行いたいときがある。
そのような場合は、Promise.all(...) を使う。やっていることは簡単だが、関数としてして実装するときに、.then についてちゃんと理解していなかったので、小一時間悩んでしまった。
// 空の解決済みPromiseを生成 <- 最初のプロミスはresolveconst
// 大元の関数に対する return
return firstFunc().then(()=>{
promises = [];
for(const id of dataIds) {
// primise をどんどんためていく
promises.push(funcPromise(id));
}
return Promise.all(promises) // 全部揃ったらresolve() を返すPromiseを戻す
}
自分のハマリポイントは .then(()=>{... return Promise.all(promises)}) しか、書かなかったところ。一番大元の関数の戻り地として return すべきところを、then で実行する関数の return としてしか書かなかったところで、うまくうごかない。。。となってしまった。
async/awaitを使って書く
Promise を完結に書く方法。自分のpleidesで、リントがエラー扱いするので使いたくないが、forループで実現するときに、これ以外の方法が思い浮かんでいない。
使う関数を
async func(){}
で宣言する。
呼び出す際に
await func()
最終更新:2020年09月13日 18:29