JavascriptのPromiseとasync/awaitについて調べる
背景
ReactでSPAを作っている。APIからデータを取得するときredux thunkとaxiosを使っているが、このときの処理がexampleを見て見よう見まねでやっていてよくわかっていない。
例えば投稿を取得するソースコードはこんな感じになっているが、return async
とか axios.get
のあとの then
とかがわかっていない。
export const fetchPosts = () => { return async (dispatch, getState) => { const posts = getState().posts dispatch(actions.startRequest(posts)) axios .get("http://example.com/posts") .then(response => { dispatch( actions.receiveData( posts, null, normalize(response.data, [schemas.post]) ) ) }) .catch(error => { dispatch(actions.receiveData(posts, error)) }) .finally(() => { dispatch(actions.finishRequest(posts)) }) } }
これを理解するためにいくつかドキュメントにあたったり実験した結果をまとめる。
そもそも同期/非同期とは
この記事がわかりやすかった。
JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる
console.log(1); setTimeout(function(){console.log(2)}, 0); console.log(3);
待ち時間がゼロなので、console.log(1)、console.log(2)、console.log(3)の順に実行されると勘違いしやすいです。 しかしあくまでlog(1)、setTimeout(〜略〜)、log(3)が順に、つまり同期的にキューに登録された後、setTimeout(〜略〜)が実行されたタイミングで、つまり非同期でlog(2)がキューに登録される
だから、出力は以下のようになる。
1 3 2
Promiseやasync/awaitを使うと
1 2 3
のように書いた順番通りに実行できるような仕組みのようだ。これを同期的と呼んでいる。
async/awaitの前にPromiseを理解するべきっぽい
適当にググっていると以下の記事を見つけた。そこにはこうある。
Promiseの使い方、それに代わるasync/awaitの使い方
async/awaitを理解するには、Promiseも知る必要がある。
なのでまずPromiseから調べる。
Promiseの基本的な使い方
コマンドライン引数がaかどうかでresolve、rejectを切り替える。
promise-basic.js
const promise = new Promise((resolve, reject) => { if (process.argv[2] === 'a') { resolve("GOOD") } else { reject("BAD") } }) promise.then(value=>console.log(value)).catch(value=>console.log(value))
実行結果
$ node promise-basic.js a GOOD $ node promise-basic.js b BAD
Promise.resolve
でPromiseのインスタンスを作れる。
promise-basic2.js
promise = Promise.resolve("GOOD") promise.then(value=>console.log(value)).catch(value=>console.log(value))
実行結果
$ node promise-basic2.js GOOD
resolveとかrejectの結果を変数に束縛できるか?
こんなような感じで書いて変数hogeに"GOOD"とか"BAD"とか入って欲しい。
promise-bind.js
const promise = new Promise((resolve, reject) => { if (process.argv[2] === 'a') { resolve("GOOD") } else { reject("BAD") } }) const hoge = promise.then(value=>value).catch(value=>value) console.log(hoge)
実行結果
$ node promise-bind.js a Promise { <pending> }
then
や catch
の中でごちゃごちゃと書くしか無いのかなあ…。
thenの中でreturnすれば外に出せるのでは説
then
で return
したら外に出せるのでは?と思ったけどダメだった。
promise-bind2.js
const promise = new Promise((resolve, reject) => { if (process.argv[2] === 'a') { resolve("GOOD") } else { reject("BAD") } }) const hoge = ( () => {promise.then(value=>return value).catch(value=> return value)} )() console.log(hoge)
実行結果
$ node promise-bind2.js a /Users/roronya/src/github.com/roronya/async-await/promise-bind2.js:10 () => {promise.then(value=>return value).catch(value=> return value)} ^^^^^^ SyntaxError: Unexpected token return at new Script (vm.js:51:7) at createScript (vm.js:136:10) at Object.runInThisContext (vm.js:197:10) at Module._compile (internal/modules/cjs/loader.js:618:28) at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10) at Module.load (internal/modules/cjs/loader.js:566:32) at tryModuleLoad (internal/modules/cjs/loader.js:506:12) at Function.Module._load (internal/modules/cjs/loader.js:498:3) at Function.Module.runMain (internal/modules/cjs/loader.js:695:10) at startup (internal/bootstrap/node.js:201:19)
resolveとかrejectとか書かなかったらどうなるのか?
何も起こらなかった。
promise-not-resolve.js
const promise = new Promise((resolve, reject) => "GOOD") promise.then(value=>console.log(value)).catch(value=>console.log(value))
実行結果
$ node promise-not-resolve.js
# 何も出ない
Promiseを使った感想
- Promiseの内部で使った値を呼び出し側に出せないのが使いづらい
- もし値を使いたいなら関数を呼んでその引数にするしか無さそう
- Promise.resolveは使い所無い気がする
- Promiseが返ってくる関数は、resolveやrejectに何が渡されるのか実装によって調べる必要があるっぽい
async/awaitの使い方
asyncだけで使う
関数にasyncと付けると Promise.resolve(関数の返り値)
と同じ結果になるっぽい。
async-basic.js
const f = async () => "GOOD" console.log(f) f().then(value => console.log(value))
実行結果
$ node async-basic.js [AsyncFunction: f] GOOD
asyncの中でだけawaitが使える
await-basic.js
const f = async () => "GOOD" const g = async () => { const y = await f() console.log('inner', y) return y } g().then(value => console.log('outer', value))
実行結果
$ node await-basic.js inner GOOD outer GOOD
classメソッドでもasync使える?
使える
async-class.js
class C { async f() { return "GOOD" } } const c = new C() console.log(c.f) c.f().then(v => console.log(v))
実行結果
[AsyncFunction: f] GOOD
async/await使った感想
- 受け取る側でPromiseを処理すればasyncは気軽に付けてよい
- await付けるとPromiseの中の値を外に持ってこれる
- が、結局asyncによってPromiseに包まれるから、もともとの呼び出し側はPromise.then.catchは書かないとダメっぽい
改訂新版JavaScript本格入門 ~モダンスタイルによる基礎から現場での応用まで
- 作者: 山田祥寛
- 出版社/メーカー: 技術評論社
- 発売日: 2016/09/30
- メディア: 大型本
- この商品を含むブログを見る