はじめに
JS...。実を言うとあまり好きになれないのですよね。理由の一つはPromise
なのかなと思っています。初学者の時に無理やり詰め込んで覚えたこちら。曖昧なところもあるままasync
, await
はもちろん、TSでも扱うようにしたら、自然と避けるようになってしまい。。。良くないので、これを機に全体をざっくり復習しようと思います。
使用テキスト
今回の使用テキストはこちらです。
かの有名なJS Primerの使用も考えたのですが、丁寧に記載がされすぎていて、かえってまとめるのが難しかったので。上記のJavaScript コードレシピ集の方は、Promise
~async
, await
を約20ページにまとめているので、非常に読みやすかったです!
引用したコードには基本的に引用元のページ数をつけています。
基本のPromise
then
で最初の処理に続く処理を記載できる
基本のPromise
の処理はこんな感じ。①resolve()
で1つ目の処理の完了させ、②.then()
で続けて処理したい内容を記載します。
const promise = new Promise((resolve) => {
console.log("最初の処理", Date.now())
setTimeout(() => {
resolve(); // 最初の処理の完了
}, 1000);
});
promise.then(() => { // then以降は最初の処理の1秒後に実行される
console.log("次の処理", Date.now())
})
JavaScript コードレシピ集 p.471 のコードに追記して使用
「最初の処理」が実行されてから1(=1000ミリ)秒後に「次の処理」が実行されます。
間にブロックする処理を挟んでも、then
に記載した処理は実行される
この時 「最初の処理」 と 「次の処理」 の間に、それを 「ブロックする処理」 を挟み込みます。
const promise = new Promise((resolve) => {
console.log("最初の処理", Date.now())
setTimeout(() => {
resolve();
}, 1000);
});
// ここを追加
setTimeout(() => {
console.log("ブロックする処理")
}, 5000);
promise.then(() => {
console.log("次の処理", Date.now())
})
通常なら、「最初の処理」 → 「ブロックする処理」 → 「次の処理」 の順に処理が走りそうですが、
promise
を使っていることで
「最初の処理」 → 「次の処理」 → 「ブロックする処理」 の順に処理が走ります。
Promiseで指定した処理にエラーが出たらcatch
で続けることができる
Promise
の中で記載した処理に失敗した時には、catch
を使って処理を続けることができます。
次のコードだと、1/2の確率でflag
はtrue
ではなくなり、「失敗時の処理」がコンソールに出力されます。
const flag = Math.random() < 0.5 && true
const promise = new Promise((resolve, reject) =>{
if (flag === true) {
resolve('成功時の処理');
} else {
reject('失敗時の処理');
}
})
promise.then((value) => {
console.log(value)
})
promise.catch((value) => {
console.log(value)
})
JavaScript コードレシピ集 p.473 のコードを改変して使用
失敗時にはこのようにコンソールに表示されるのですが、どうしてエラー文まで出るのかは謎です。
直列処理とasync
, await
まずはサンプルコード
.then
でメソッドチェーン的にPromiseの処理を続けて書くことができます。
Promise.resolve()
.then(
() =>
new Promise((resolve) => {
setTimeout(() => {
console.log('1つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000);
})
)
.then(
() =>
new Promise((resolve) => {
setTimeout(() => {
console.log('2つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000);
})
)
JavaScript コードレシピ集 p.477 のコードを抜粋
そして、直列処理のpromiseはasync
, await
構文を使うとより簡潔に書けます。
async function start() {
await new Promise((resolve) => {
setTimeout(() => {
console.log('1つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000);
})
await new Promise((resolve) => {
setTimeout(() => {
console.log('2つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000);
})
}
start();
JavaScript コードレシピ集 p.478 のコードを抜粋
詳細確認
わーい、簡単♪ ...と思いきや、謎もやはり多いですよね。まずはこの部分。
Promise.resolve()
.then(
() =>
new Promise((resolve) => { // ←引数resolveとは?
setTimeout(() => {
console.log('1つ目のPromise', new
//... 以下略
突如出てきたように見える、引数のresolve
ってなんやねん!?ってなりませんでしたか?(私はなりました)。
Promise
では、直前のPromise
の成功時に返された値を、次のthen
の引数として使えるそうなので、その値です。
なので、次のような直列処理の式も成立します。
Promise.resolve(1) // ここのresolveは成功時に1を返すpromiseを作る
.then((value) => {
console.log(value); // 1が表示される
return value * 2
})
.then(
(value) => {
console.log(value); // 2が表示される
return value * 2
})
JavaScript Primer P.277 のコードを改変して使用
そして、async
, await
構文についてですが、まず、
-
await
は右辺の式が実行されるまで次の処理を待つ機能です -
async
はpromise
インスタンスを返すメソッドです。
なので、こちらのasync
構文は一つ目のPromiseを行なって、二つ目のPromiseを実行するという一連の処理の流れを持ったPromiseインスタンスを返す構文となります。
const start = async () => {
await new Promise((resolve) => {
setTimeout(() => {
console.log('1つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000); // 1. この処理が終わったら
})
await new Promise((resolve) => {
setTimeout(() => {
console.log('2つ目のPromise', new Date().toLocaleTimeString());
resolve();
}, 1000); // 2. この処理を実行して
})
}
start(); // 1~2の一連の流れが詰まったメソッド
まとめ
以上となりましたが、ざっくりとPromise
の流れを復習しました。今だからわかることも多いけど、初学者の時には分かりにくかったこともあり、やはり定期的に復習は大事だなと思いました。
手元にあるTSの書籍についても非同期処理の記述があるので、引き続きTSにおけるPromiseの扱いについて復習したいと思います。