JavaScript の Promise 試してみた


モダン JavaScript で実装するとき ``async/await`` を非同期処理を扱うために使用しますが、``async/await``の挙動を理解するためにも、まずは``Promise``の挙動を確認してみます。

## promise

``Promise``とは:
>Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。


``Promise``を返す関数を作成

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        console.log('promise')
    })
}

resolveAfter2Seconds()
console.log('==end==')
```

結果

```terminal
# node async.js
starting slow promise
promise
==end==
```

2秒後に実行される関数を``Promise``でラップする

```js
function resolveAfter2Seconds() {
    console.log("starting promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

resolveAfter2Seconds()
console.log('==end==')
```

結果. 実行結果が変わった。``Promise``によって非同期に実行されている.

```terminal
# node async.js
starting promise
==end==
2秒後に実行されます
#
```

``then``メソッドを使用して``resolve``を受け取る

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

resolveAfter2Seconds()
  .then(resolve => console.log(resolve))
console.log('==end==')
```

結果

```terminal
# node async.js
starting promise
==end==
2秒後に実行されます
slow
#
```

1秒後に実行される関数をラップした``Promise``を追加する

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

resolveAfter2Seconds()
resolveAfter1Second()

console.log('==end==')
```

結果. 処理の実行順序にかかわらず非同期で実行されている.

```terminal
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
2秒後に実行されます
```

``Promise.all()``を使用すると非同期処理を平行で処理し、すべての処理が終了するのを待ってから結果を実行できる

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}


function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

Promise.all([resolveAfter2Seconds(), resolveAfter1Second()])
  .then(([result1, result2]) => {
    console.log(result1)
    console.log(result2)
  })

console.log('==end==')
```

結果

```terminal
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
2秒後に実行されます
slow
fast
```

## 直列実行, 逐次実行

``reduce``を使うことで直列的に実行できる

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}


function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

[resolveAfter2Seconds, resolveAfter1Second]
  .reduce((p, f) => p.then(f), Promise.resolve())
  .then(result3 => console.log(result3))

console.log('==end==')
```

結果

```terminal
# node async.js
==end==
starting slow promise
2秒後に実行されます
starting fast promise
1秒後に実行されます
fast
```

``async/await``を使うと簡潔に書ける

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}


function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

async function sequentialStart() {
  const slow = await resolveAfter2Seconds()
  console.log(slow)

  const fast = await resolveAfter1Second()
  console.log(fast)
}

sequentialStart()
console.log('==end==')
```

結果

```terminal
# node async.js
starting slow promise
==end==
2秒後に実行されます
slow
starting fast promise
1秒後に実行されます
fast
```

## async と await

``async``は``Promise``を返す(返値は暗黙的に``Promise.resolve``でラップされている)

``await``は非同期関数の実行を一時停止して``Promise``の解決を待つ. 非同期関数が一時停止している間も, 呼び出し側の関数は実行が続く.

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

async function asyncCall() {
  console.log('calling')
  const result = await resolveAfter2Seconds()
  console.log(result)
}

asyncCall()
console.log('==end==')
```

結果

```terminal
# node async.js
calling
starting slow promise
==end==
2秒後に実行されます
slow
```

``await``を使用しないと``async``関数内の実行は続いく

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

async function asyncCall() {
  console.log('calling')
  const result = resolveAfter2Seconds()
  console.log(result)
}

asyncCall()
console.log('==end==')
```

結果

```terminal
# node async.js
calling
starting slow promise
Promise { <pending> }
==end==
2秒後に実行されます
```

``async``関数の戻り値を確認

```js
async function asyncCall() {
  console.log('calling')
  return 'success'
}

console.log(asyncCall())
console.log('==end==')
```

結果

```terminal
# node async.js
calling
Promise { 'success' }
==end==
```

``Promise``で返ってくる。``Promise.resolve()``でラップされているので``then``メソッドで繋げると

```js
async function asyncCall() {
  console.log('calling')
  return 'success'
}

asyncCall()
  .then(result => console.log(result))

console.log('==end==')
```

結果. ``async``関数の戻り値を実行できる.

```terminal
# node async.js
calling
==end==
success
```

### 並列で実行させる

``Promise.all``を使用して並列で実行させる

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}

function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

async function parallel() {
  await Promise.all([
    (async() => console.log(await resolveAfter2Seconds()))(),
    (async() => console.log(await resolveAfter1Second()))()
  ])

  console.log('parallel end')
}
```

結果

``Promise.all``を使用しているので, すべてのプロミスが解決されるまで次の処理が実行されていない.

```terminal
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
fast
2秒後に実行されます
slow
parallel end
```

非同期関数を並列で実行させて返り値を受け取る

```js
function resolveAfter2Seconds() {
    console.log("starting slow promise")
    return new Promise(resolve => {
        setTimeout(function() {
            resolve("slow")
            console.log("2秒後に実行されます")
        }, 2000)
    })
}


function resolveAfter1Second() {
  console.log("starting fast promise")
  return new Promise(resolve => {
    setTimeout(function() {
      resolve("fast")
      console.log("1秒後に実行されます")
    }, 1000)
  })
}

async function parallel() {
  const result = await Promise.all([
    resolveAfter2Seconds(), resolveAfter1Second()
  ])
    .then(([result1, result2]) => {
      return ('resolve : ' + result1 + result2)
      // return Promise.resolve('resolve : '  + result1 + result2) ← これでも可
      // return new Promise(resolve => {
      //   resolve('resolve : '  + result1 + result2)  ← これでも可
      // })
    })

  console.log('parallel end')
  return result
}

parallel()
  .then(result => console.log(result))


console.log('==end==')
```

結果

```terminal
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
2秒後に実行されます
parallel end
resolve : slowfast
```

## 参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function