(標準組み込みオブジェクト)
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise オブジェクトは、非同期処理の完了(もしくは失敗)の結果およびその結果の値を表す。
Promiseには、PromiseStatusというstatusがあり、3つのstatusがある。
- pending:未解決 (処理が終わるのを待っている状態)
- resolved:解決済み (処理が終わり、無事成功した状態)
- rejected:拒否 (処理が失敗に終わってしまった状態)
new Promise()で作られたPromiseオブジェクトは、pendeingというPromiseStatusで作られる。
処理が成功した時に、PromiseStatusはresolvedに変わり、thenに書かれた処理が実行される。
処理が失敗した時は、PromiseStatusがrejectedに変わり、catchに書かれた処理が実行される。
Promiseインスタンスの作成
const promise = new Promise((resolve, reject) => {});
Promiseの引数には関数を渡し、その関数の第一引数にresolveを設定し、第二引数にrejectを任意で設定する(resolveもrejectも関数である)。
インスタンス作成直後のPromiseStatusは、pendeingである。
resolveさせる
// rejectは今回使わないため、引数から削除
const promise = new Promise((resolve) => {
resolve();
}).then(() => {
console.log("resolveしたよ");
});
>> resolveしたよ
resolve()を呼び出すことによって、PromiseStatusはresolvedに変わり、thenの処理が実行される。
resolve関数は引数を受け取ることができ、次に呼ばれるメソッド(then)の第一引数に渡してあげることができる。
const promise = new Promise((resolve) => {
// 引数に文字列を渡す
resolve("resolveしたよ");
}).then((val) => {
// 第一引数にて、resolve関数で渡した文字列を受け取ることができる
console.log(val);
});
>> resolveしたよ
rejectさせる
const promise = new Promise((resolve, reject) => {
reject();
})
.then(() => {
console.log("resolveしたよ");
})
.catch(() => {
console.log("rejectしたよ");
});
>> rejectしたよ
上記の例ではthenの処理は実行されず、catchの処理が実行される。
しかし、PromiseStatusではrejectedにならない。
実は、catchにて実行される関数がreturnした値をresolveする。
簡単にいうと、catchはエラーを返したら満足して、解決済みの扱いとなる。
ゆえに、catchにて返されたpromiseオブジェクトのPromiseStatusはresolveになる。
Promiseのメソッドチェーン
処理が成功した時にthenに書かれた処理が実行され、処理が失敗した時はcatchに書かれた処理が実行されるが、それらの処理の後にまた別のthenを実行することができる。
さらに、returnで返した値を第一引数として次のthenに渡すことが可能である。
const promise = new Promise((resolve, reject) => {
resolve("test");
})
.then((val) => {
console.log(`then1: ${val}`);
return val;
})
.catch((val) => {
console.log(`catch: ${val}`);
return val;
})
.then((val) => {
console.log(`then2: ${val}`);
});
>> then1: test
>> then2: test
resolveされたため、1つ目のthenが実行されたあと、returnでvalを受け取り、2つ目のthenが実行された。
const promise = new Promise((resolve, reject) => {
reject("test");
})
.then((val) => {
console.log(`then1: ${val}`);
return val;
})
.catch((val) => {
console.log(`catch: ${val}`);
return val;
})
.then((val) => {
console.log(`then2: ${val}`);
});
>> catch: test
>> then2: test
rejectされたため、1つ目のthenは処理されず、1つ目のcatchが実行されたあとに2つ目のthenが実行された。
Promise.allとPromise.race
Promise.all
Promise.all()は配列でPromiseオブジェクトを渡し、全てのPromiseオブジェクトがresolvedになったら次の処理に進む。
const promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log("promise1おわったよ!");
});
const promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 3000);
}).then(() => {
console.log("promise2おわったよ!");
});
Promise.all([promise1, promise2]).then(() => {
console.log("全部おわったよ!");
});
>> promise1おわったよ!
>> promise2おわったよ!
>> 全部おわったよ!
Promise.race
Promise.race()はPromise.all()と同じく配列でPromiseオブジェクトを渡し、どれか1つのPromiseオブジェクトがresolvedになったら次に進む。
const promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000);
}).then(() => {
console.log("promise1おわったよ!");
});
const promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 3000);
}).then(() => {
console.log("promise2おわったよ!");
});
Promise.race([promise1, promise2]).then(() => {
console.log("どれか一つおわったよ!");
});
>> promise1おわったよ!
>> どれか一つおわったよ!
>> promise2おわったよ!
async/awaitを使う
Promiseの処理は、Promiseインスタンスを生成したり、resolve/rejectしたりするため、場合によっては冗長的になってしまう。
async/awaitを使うとPromiseの処理をより簡潔に書くことができる。
// async function を定義
const alwaysLateBoy = async (ms) => {
await new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
})
// Promiseの結果が返ってくるまで実行されない
console.log(`すまん!${ms}ms待たせたな。`)
};
async
asyncは非同期関数を定義する関数宣言であり、関数の頭につけることで、Promiseオブジェクトを返す関数にすることができる。そのような関数をasync functionという。
const asyncFunc = async () => {
return 1;
};
async functionが値をreturnした場合、Promiseは戻り値をresolveし、その値はPromiseValueとして扱われる。
await
awaitは、Promiseオブジェクトが値を返すのを待つ演算子である。awaitは必ず、async function内で使う。
const asyncFunc = async () => {
let x, y;
x = new Promise(resolve => {
setTimeout(() => {
resolve(1);
}, 1000 )
})
y = new Promise(resolve => {
setTimeout(() => {
resolve(1);
}, 1000 )
})
console.log(x + y)
}
>> NaN
上記の例では、Promiseがresolveするのを待つ処理を書いてないため、console.log(x + y)ではNaNが返ってくる。
この処理を、Promiseがresolveするのを待つようにするには、このPromiseオブジェクトが全て実行完了するのを待つPromise.allを使えばできそうだが、awaitを使うともっと簡単に実装できる。
const asyncFunc = async () => {
let x, y;
// Promiseがresolveするまで待機
x = await new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
// Promiseがresolveするまで待機
y = await new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
console.log(x + y);
};
>> 2
console.log(x + y)は、上二つのPromiseが値を返すのを待ってから実行されるため、実行結果は2が返ってくる。