(基本的に読者はPromise
やasync
, await
について知っている前提です。この「Promise
について」で説明はしていますが、とにかくはしょり過ぎているので、これらを正しいと感じれらるかだけの利用に限ってください=正しいと感じられたら次に進んでください。学習には向きません。)
Promise
はcallback地獄を回避するために生まれた。
Promise
のインスタンスには3種類の状態があり、pending
, resolved
, rejected
がある。
例えば、
var promise = new Promise(function(resolve, reject){
console.log('abc')
})
とした場合のpromise
は、resolve
を内部で呼んでいないのでpending
状態のpromise
となる。
一方、次のようにすると内部でresolve
が呼ばれているので、resolved
なpromise
となる。
var promise = new Promise(function(resolve, reject){
resolve('abc')
})
(特に上記のpromise
の場合、'abc'
を引数にしているので「'abc'
でresolveされたpromise」となる。)
一方、次のようにすると内部でreject
が呼ばれているので、rejected
なpromise
となる。
var promise = new Promise(function(resolve, reject){
reject('abc')
})
基本的なPromise
の使い方としては、
promise.then(関数1).then(関数2).then(関数3)
のようにやる。何やっているかというと、promise
がresolved
になると関数1
が呼ばれる。
(関数1
が実行されたら関数2
、関数3
と進む。関数1
の引数はpromise
がresolve
された値、関数2
の引数は関数1
の返り値、関数3
の引数は関数2
の返り値となる。(最初だけ引数のルールが違うが、基本的にresolved
されたら次へ、という風になっている))
(rejected
だとthen
の代わりにcatch
が呼ばれる。reject
についてはこの文章では触れない。)
ここでは落とし穴しか説明しないので、これ以上の内容については各自でMDNのドキュメントなどを読んでください。
console.log(new Promise(function(){
console.log('in promise');
}))
とやると、'in promise'
が表示された後、pending
なpromise
が表示される。
同じことだけど、
var promise = new Promise(function(resolve, reject){
resolve('new promise');
});
console.log(promise)
とやると、'new promise'
でresolve
されたpromise
が表示される。
つまり、new Promise
で渡した引数関数は同期的に実行され、終わったら(もちろん同期的に)promise
として返る。
var promise = new Promise(function(resolve, reject){
resolve('new promise');
}).then(function(){
console.log('in then');
});
console.log(promise)
とやると、pending
なpromise
が表示された後、'in then'
が表示される。
つまり、実行される順番は、
同期的に
new Promise
で与えられた関数
=>
new Promise
の外側
その後すぐ非同期的に
=>
then
チェーンの中身
(then
は強制的に非同期。)
async function asyncFunc(a,b,c) {
return expression
}
は、
function asyncFunc (a,b,c) {
return new Promise(function (resolve) {
var result = expression
resolve(result)
});
}
と同じ。
なので、async
の中身(expression)は同期的に実行される(new Promise
の引数関数に相当するので)。
async function asyncFunc(){
expression1
var result = await expression
expression2
}
からの、
asyncFunc()
は、
(new Promise(function(resolve){
expression1
resolve(expression)
})).then(function(result){
expression2
})
と同じ。
await
の本質は、実質的にthen
の中身をawait
の下に同じレイヤーに書ける、ということ。
await
を何度も使うと、実質的にはその度にthen
チェーンが繋がれていく。
なので、
async function asyncFunc(){
console.log('new promise')
}
var promise = asyncFunc()
console.log(promise)
とやると'new promise'
と表示された後、undefined
でresolve
されたpromise
が表示されるが、
async function asyncFunc(){
console.log(await 'new promise');
})
var promise = asyncFunc()
console.log(promise)
とやるとpending
されたpromise
が表示された後、'new promise'
と表示される。
(await
の右側は同期的に実行されるが、返り値を利用できるタイミング以降の実行は非同期になっている。)
async function asyncFunc(){} var result = await asyncFunc().then.catch とした場合、syncFunc()だけは同期的に実行され、後のthenやcatchは非同期的に実行される。 なので、実質的にresultの側と言える(thenの側=非同期側)。
また、この場合はasyncFuncがasyncであることも大切(もちろんプロミスを返すだけでも良い)で、asyncでないとプロミスを返さず、thenやcatchで繋げない。
で、この方法の何が嬉しいかというと、try{}catch{}の代わりになる、ということ。 参考:https://qiita.com/akameco/items/cc73afcdb5ac5d0774bc
つまり、tryの代わりにPromise、Promiseの代わりにasync、という流れ。 別にawaitは必須ではないけど、try/catchは同期なので、それと同じことをしようとすると必要、って感じですね。
async
にせよnew Promise
にせよ、同期的な処理ばかり書いているとそれらは結局同期的にしか実行されないので、非同期の意味では意味ないけど動かないこともない。
await
やthen
を入れるとそこからは非同期になるので一気に使うメリットが出て来る(コールバック地獄をなくす目的が達成される)。
なんでasync
の中にしかawait
を入れられないのかはちょっとまだ分かっていない。
別にasync
はPromise
のラッパーでしかないのだから、Promise
の中にawait
を入れても良いと思うのだけど。。