この記事は以下の書籍を参考に執筆しました
プロミスでthenメソッドを呼び出すときには、引数として関数を渡し、この関数から返されるものはなんであってもチェンの次のthenの値になるが、
これには例外が1つある。
thenに渡された関数がプロミスを返す場合、そのプロミスは、値が解決または拒否されルマで待機してから同じことを行う。
function wait(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds)
})
}
const time = new Date().getTime()
const logTime = () => {
const seconds = (new Date().getTime() - time)
console.log(seconds / 1000, 'have elapsed')
}
wait(5000)
.then(() => {
logTime();
return wait(5000)
})
.then(() => {
logTime();
return wait(2000)
})
.then(() => {
logTime();
})
5.003 "have elapsed"
10.009 "have elapsed"
12.012 "have elapsed"
.then(() => {
logTime();
wait(5000)
})
ここでは更に5秒待機するように設定されたプロミスを返す。
ここまでは合計で10秒待つことになる。
続いて2秒対して最後まで実行されるのに12秒かかる。
この方法がうまくいくのはthenに渡された関数がプロミスを返す場合に限られる。
下はプロミスを返さない場合。
function wait(milliseconds) {
return new Promise((resolve) => {
setTimeout(resolve, milliseconds)
})
}
const time = new Date().getTime()
const logTime = () => {
const seconds = (new Date().getTime() - time)
console.log(seconds / 1000, 'have elapsed')
}
wait(5000)
.then(() => {
logTime();
wait(5000)
})
.then(() => {
logTime();
wait(2000)
})
.then(() => {
logTime();
})
5.002 "have elapsed"
5.002 "have elapsed"
5.002 "have elapsed"
wait関数が返されていないため、待機しない。
##エラーキャッチ
thenの2つ目のパラメータにエラー時のコールバックを渡せるということは、数珠つなぎにした場合複数のエラーハンドラが必要なのか?
その必要はなく、プロミスが拒否された場合、そのプロミスは最初のエターハンドラが検出されるまでその上にあるプロミスをさかのぼる
Promise.resolve()
.then(() => console.log('A'))
.then(() => Promise.reject('X'))
.then(() => console.log('B'))
.then(null, err => console.log('caught:', err))
.then(() => console.log('C'))
出力はこうなる
A
caught: X
C
Bが出力されていないことに注目
Bの出力はrejectとエラーハンドラの間で発生している。
しかしCは出力されている。
プロミスが拒否されると、そのことがキャッチされるまで、
その後にあるプロミスは解決されないから。
プロミスの拒否がキャッチされた時点で
それよりも後ろにあるプロミスの解決が可能になる。
.then(null, err => console.log('caught:', err))
エラーハンドラを見てみると
1つの目引数にnull
が渡されている。
このステップではエラーをキャッチすることが唯一の目的だから。
そのためのメソッドとしてcatchがあり、それで書き換えると以下のようになる。
Promise.resolve()
.then(() => console.log('A'))
.then(() => Promise.reject('X'))
.then(() => console.log('B'))
.catch(err => console.log('caught:', err))
.then(() => console.log('C'))
プロミスを拒否するためにPromise.rejectメソッドを使ったり、プロミス内部でrejectメソッドを呼ぶ必要はない。
Javascriptエラーが発生すればプロミスはすべて拒否される。
Promise.resolve()
.then(() => {throw 'My Error'})
.catch(err => console.log('catch:', err))//catch: My Error
別の例を見てみる
ajax('/my-data.json')
.then(
res => {
throw 'Some Error'
},
err => {
console.log('First catcher:', err) //エラーがキャッチされない
})
.catch(err => {
console.log('Second catcher:', err) //エラーがキャッチされる
})
thenに2つの関数を渡すと
1つ目の関数がプロミス解決時に実行
2つ目の関数がプロミス拒否時に実行される
しかし1つ目の関数でエラーが発生した場合、
2つ目の関数はそのエラーをキャッチしない
つまり、エラーをキャッチするには次のthenでキャッチしなくちゃいけない
#参考文献