俺、サービス売って家買うんだ

Swift, Kotlin, Vue.js, 統計, GCP / このペースで作ってればいつか2-3億で売れるのがポっと出来るんじゃなかろうか

async/awaitが手になじむまで色々書いてみるよ〜

f:id:ie-kau:20170930235521p:plain:w500


現在運用中のプロジェクトのNodeのバージョンを7から8にあげたのでPromiseで書いていた箇所を徐々にasync/awaitに置き換えています。

簡単に書ける一方でPromsieに慣れすぎていたため、「これ並列処理になるんだっけ」とか、「てか、これ動く?」見たいなレベルで手になじまなかったので色々ためして馴染もうと思います。

利用するタイマー関数

function timer(ms, name) {
  console.log(`name: ${name} start!`)
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(name), ms)
  })
}

Case.1

  • awaitを書いて非同期処理の関数を並べて書く
(async () => {
  const result1 = await timer(10000, 'a')
  const result2 = await timer(10000, 'b')
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.2

  • awaitを書いて非同期処理の関数の戻り値でデストラクチャリング
(async () => {
  const [result1, result2] = [await timer(10000,'a'), await timer(10000, 'b')]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 20000ms

Case.3

  • Promise.allをawaitしてデストラクチャリング
(async () => {
  const [result1, result2] = await Promise.all([timer(10000, 'a'), timer(10000, 'b')])
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

Case.4

  • 先にpromiseを作ってデストラクチャリング
(async () => {
  const p1 = timer(10000, 'a')
  const p2 = timer(10000, 'b')
  const [result1, result2] = [await p1, await p2]
  console.log(result1, result2)
})()

// name: a start!
// name: b start!
// a b
// 経過時間: 10000ms

これは並列処理になる!

Case.5

  • for文の中でawaitさせる
(async () => {
    for (let {ms, name} of [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]) {
      const result = await timer(ms, name)
      console.log(result)
    }
})()

// name: a start!
// a
// name: b start!
// b
// 経過時間: 20000ms

直列処理になる。

Case.5

  • forEach内のラムダで実行する
(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .forEach(async ({ms, name}) => {
      const result = await timer(ms, name)
      console.log(result)
    })
  console.log('c')

})()

// name: a start!
// name: b start!
// c
// a
// b

並列処理になるがcはラムダ内のawaitを待つことはない。

Case.6

  • mapの戻りでawaitさせてみる
(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(async ({ms, name}) =>  await timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ Promise { <pending> }, Promise { <pending> } ]
// 経過時間: (ほぼ)0ms

ただのpromiseの配列が帰ってくる。 当然だけど↓と同じ。

(async () => {
  const results = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
  console.log(results)

  // name: a start!
  // name: b start!
  // [ Promise { <pending> }, Promise { <pending> } ]
})()

Case.8

(async () => {
  [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
    .map(({ms, name}) => timer(ms, name))
    .forEach(async promise => {
      const result = await promise
      console.log(result)
    })
  console.log('c')
})()

// name: a start!
// name: b start!
// c
// a
// b

cは即時出力。timerは並列で実行されるが、forEachのラムダ内はawaitできる。

Case.9

  • promiseの配列をPromise.allの引数に与える
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(({ms, name}) => timer(ms, name))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

これが一番シンプルに並列処理の結果を直列に受け取れる気がする。

Case.10

  • mapに渡すラムダ内でasync/awaitをする
(async () => {
  const sample = [{ms: 10000, name: 'a'}, {ms: 10000, name: 'b'}]
  const results = await Promise.all(sample.map(async ({ms, name}) => await timer(ms, name)))
  console.log(results)
})()

// name: a start!
// name: b start!
// [ 'a', 'b' ]
// 経過時間:10000ms

そもそもtimerもasync functionもpromiseを返すので二重定義になって意味ない。

完全に理解した!!!!!