Promise をつくって返す
このレッスンで分かること
new Promiseに渡した関数の中でresolveを呼ぶと、値が確定します- Promise は戻り値なので、変数に入れたり関数から返したりできます
呼んだ瞬間には、まだ中身が無い
時間のかかる処理を関数にすると、return で答えを返せません。答えが出るのは何百ミリ秒か先で、return の行はもう過ぎているからです。そこで JavaScript は、値の代わりに「あとで値が入る箱」を返す形にそろえました。これが Promise です。
JavaScript
const box = fetchHeadline();
console.log(box); // Promise { <pending> }呼んだ瞬間に返ってくるのは箱だけで、中身はまだ空です。この状態を pending と呼びます。大事なのは、この箱そのものが値だという点です。変数に入れられますし、関数から返せますし、配列に並べることもできます。
箱を自分で作る
自分で箱を作るときは new Promise を使い、引数に関数を 1 つ渡します。この関数はすぐその場で実行され、resolve と reject という 2 つの関数を受け取ります。
JavaScript
function wait(ms) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve("待ち終わりました");
}, ms);
});
}resolve("待ち終わりました") を呼んだ瞬間に、箱の中身が確定します。return の位置に注目してください。返しているのは resolve の結果ではなく、new Promise で作った箱そのものです。箱は関数を呼んだ直後に返り、中身だけがあとから入ります。resolve に渡した値がそのまま中身になり、何も渡さなければ undefined になります。
| 状態 | 意味 | どうなると移るか |
|---|---|---|
| pending | まだ返事が来ていない | 作った直後はこれ |
| fulfilled | 成功の返事が来た | resolve が呼ばれた |
| rejected | 失敗の返事が来た | reject が呼ばれた |
pending から動くのは一度きりです。resolve を 2 回呼んでも、2 回目以降は黙って無視されます。エラーにならないぶん気付きにくいので、確定させる場所は 1 つに絞ってください。今回作る箱は必ず成功する側なので、使うのは resolve だけです。
箱を開ける
箱の中身を外から覗く方法はありません。box.text のように書いても取り出せません。中身に触るには await を書きます。await は async を付けた関数の中でだけ使えます。
JavaScript
async function showHeadline() {
const text = await wait(100);
console.log(text); // 待ち終わりました
}await wait(100) の行で、この関数はいったん中断します。中断しているあいだ作業員は解放されているので、画面は固まりません。中身が確定すると続きから再開し、text に文字列が入ります。await の左の変数に入るのは、箱ではなく中身です。
await が入るより前は、.then に関数を渡して中身を受け取っていました。他人のコードやライブラリの説明で今でもよく見かけるので、対応だけ押さえておきます。
JavaScript
wait(100).then(function (text) {
console.log(text);
});then に渡した関数は、中身が確定したときに、その中身を引数として呼ばれます。自分で書くときは await のほうが読みやすいので、then は読めれば十分です。
もう 1 つ、async を付けた関数の戻り値は必ず Promise になります。中で配列を return しても、呼ぶ側が受け取るのは「あとで配列が入る箱」です。だから呼ぶ側にも await が要ります。
演習では、箱を作る部分に集中する
まず上の wait と showHeadline の例を読み、箱を返す処理と中身を待つ処理を区別しましょう。演習の selectPublished は公開中の作品を選び、titlesOf は届いた配列をタイトルの配列に変える補助関数です。loadPublishedTitles の待機も用意してあります。自分で書くのは、箱を作って返し、タイマーの中で値を確定させる部分です。await を使って順番に処理を書く練習は次の回で行います。
例えば、タイマーを使わずに値を確定する箱は次のように作れます。
JavaScript
const signal = new Promise(function (resolve) {
resolve("準備完了");
});
console.log(signal); // 値が決まった Promise変数に入るのは文字列ではなく箱です。演習ではタイマーで確定を遅らせ、公開中の作品の配列を箱の中身にします。
よくある間違い
resolveを呼び忘れる — 箱は pending のまま永遠に確定せず、awaitした側も再開しません。エラーが出ないぶん気付けず、採点では時間切れになります。new Promiseを書いたら、resolveがどの行で呼ばれるかを必ず目で追ってください- 箱のまま使ってしまう —
awaitを書き忘れると、変数に入るのは中身ではなく Promise です。配列やオブジェクトのメソッドが無いというエラーが出たら、まずawaitの書き忘れを疑ってください
要件
- fetchPublishedWorks(works) で new Promise を作って返す
- Promise に渡す関数の中で setTimeout を使い、100 ミリ秒後に準備済みの published を resolve する
- 準備済みの selectPublished、titlesOf、loadPublishedTitles はそのまま使う。公開中の作品が無ければ空の配列が届く