モダン JavaScript で実装するとき async/await
を非同期処理を扱うために使用しますが、async/await
の挙動を理解するためにも、まずはPromise
の挙動を確認してみます。
promise
Promise
とは:
Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。
Promise
を返す関数を作成
function resolveAfter2Seconds() {
console.log("starting slow promise")
return new Promise(resolve => {
console.log('promise')
})
}
resolveAfter2Seconds()
console.log('==end==')
結果
# node async.js
starting slow promise
promise
==end==
2秒後に実行される関数をPromise
でラップする
function resolveAfter2Seconds() {
console.log("starting promise")
return new Promise(resolve => {
setTimeout(function() {
resolve("slow")
console.log("2秒後に実行されます")
}, 2000)
})
}
resolveAfter2Seconds()
console.log('==end==')
結果. 実行結果が変わった。Promise
によって非同期に実行されている.
# node async.js
starting promise
==end==
2秒後に実行されます
#
then
メソッドを使用してresolve
を受け取る
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==')
結果
# node async.js
starting promise
==end==
2秒後に実行されます
slow
#
1秒後に実行される関数をラップしたPromise
を追加する
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==')
結果. 処理の実行順序にかかわらず非同期で実行されている.
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
2秒後に実行されます
Promise.all()
を使用すると非同期処理を平行で処理し、すべての処理が終了するのを待ってから結果を実行できる
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==')
結果
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
2秒後に実行されます
slow
fast
直列実行, 逐次実行
reduce
を使うことで直列的に実行できる
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==')
結果
# node async.js
==end==
starting slow promise
2秒後に実行されます
starting fast promise
1秒後に実行されます
fast
async/await
を使うと簡潔に書ける
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==')
結果
# node async.js
starting slow promise
==end==
2秒後に実行されます
slow
starting fast promise
1秒後に実行されます
fast
async と await
async
はPromise
を返す(返値は暗黙的にPromise.resolve
でラップされている)
await
は非同期関数の実行を一時停止してPromise
の解決を待つ. 非同期関数が一時停止している間も, 呼び出し側の関数は実行が続く.
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==')
結果
# node async.js
calling
starting slow promise
==end==
2秒後に実行されます
slow
await
を使用しないとasync
関数内の実行は続いく
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==')
結果
# node async.js
calling
starting slow promise
Promise { <pending> }
==end==
2秒後に実行されます
async
関数の戻り値を確認
async function asyncCall() {
console.log('calling')
return 'success'
}
console.log(asyncCall())
console.log('==end==')
結果
# node async.js
calling
Promise { 'success' }
==end==
Promise
で返ってくる。Promise.resolve()
でラップされているのでthen
メソッドで繋げると
async function asyncCall() {
console.log('calling')
return 'success'
}
asyncCall()
.then(result => console.log(result))
console.log('==end==')
結果. async
関数の戻り値を実行できる.
# node async.js
calling
==end==
success
並列で実行させる
Promise.all
を使用して並列で実行させる
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
を使用しているので, すべてのプロミスが解決されるまで次の処理が実行されていない.
# node async.js
starting slow promise
starting fast promise
==end==
1秒後に実行されます
fast
2秒後に実行されます
slow
parallel end
非同期関数を並列で実行させて返り値を受け取る
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==')
結果
# 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