Skip to content

Instantly share code, notes, and snippets.

@tetz-akaneya
Last active June 26, 2018 08:25
Show Gist options
  • Save tetz-akaneya/797475e3bfd947399c50dacaf16428e5 to your computer and use it in GitHub Desktop.
Save tetz-akaneya/797475e3bfd947399c50dacaf16428e5 to your computer and use it in GitHub Desktop.

はじめに。Promiseについて

(基本的に読者はPromiseasync, awaitについて知っている前提です。この「Promiseについて」で説明はしていますが、とにかくはしょり過ぎているので、これらを正しいと感じれらるかだけの利用に限ってください=正しいと感じられたら次に進んでください。学習には向きません。)

Promiseはcallback地獄を回避するために生まれた。

Promiseのインスタンスには3種類の状態があり、pending, resolved, rejectedがある。

例えば、

var promise = new Promise(function(resolve, reject){
  console.log('abc')
})

とした場合のpromiseは、resolveを内部で呼んでいないのでpending状態のpromiseとなる。

一方、次のようにすると内部でresolveが呼ばれているので、resolvedpromiseとなる。

var promise = new Promise(function(resolve, reject){
  resolve('abc')
})

(特に上記のpromiseの場合、'abc'を引数にしているので「'abc'でresolveされたpromise」となる。)

一方、次のようにすると内部でrejectが呼ばれているので、rejectedpromiseとなる。

var promise = new Promise(function(resolve, reject){
  reject('abc')
})

基本的なPromiseの使い方としては、

promise.then(関数1).then(関数2).then(関数3)

のようにやる。何やっているかというと、promiseresolvedになると関数1が呼ばれる。 (関数1が実行されたら関数2関数3と進む。関数1の引数はpromiseresolveされた値、関数2の引数は関数1の返り値、関数3の引数は関数2の返り値となる。(最初だけ引数のルールが違うが、基本的にresolvedされたら次へ、という風になっている))

(rejectedだとthenの代わりにcatchが呼ばれる。rejectについてはこの文章では触れない。)

ここでは落とし穴しか説明しないので、これ以上の内容については各自でMDNのドキュメントなどを読んでください。

ここから落とし穴。

new Promiseについて(同期・非同期)

console.log(new Promise(function(){
  console.log('in promise');
}))

とやると、'in promise'が表示された後、pendingpromiseが表示される。

同じことだけど、

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)

とやると、pendingpromiseが表示された後、'in then'が表示される。

つまり、実行される順番は、

同期的に new Promiseで与えられた関数 => new Promiseの外側

その後すぐ非同期的に => thenチェーンの中身

thenは強制的に非同期。)

asyncについて。awaitが入っていない場合。

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の引数関数に相当するので)。

awaitについて。

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'と表示された後、undefinedresolveされたpromiseが表示されるが、

async function asyncFunc(){
  console.log(await 'new promise');
})
var promise = asyncFunc()
console.log(promise)

とやるとpendingされたpromiseが表示された後、'new promise'と表示される。

awaitの右側は同期的に実行されるが、返り値を利用できるタイミング以降の実行は非同期になっている。)

async/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にせよ、同期的な処理ばかり書いているとそれらは結局同期的にしか実行されないので、非同期の意味では意味ないけど動かないこともない。

awaitthenを入れるとそこからは非同期になるので一気に使うメリットが出て来る(コールバック地獄をなくす目的が達成される)。

なんでasyncの中にしかawaitを入れられないのかはちょっとまだ分かっていない。

別にasyncPromiseのラッパーでしかないのだから、Promiseの中にawaitを入れても良いと思うのだけど。。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment