async function は Promose を返す
return
すると返される値がPromise.resolve
されるthrow
すると値や例外がPromise.reject
される- 何も返さないと関数終了時に
undefined
がreturn
(resolove
)される
// return = Promise.resolve() async function resolveFunc() { return 'this resolve'; } resolveFunc() .then((res) => console.log(res)) .catch((err)=> console.log(`ERROR ${err}`)); // => this resolve // throw = Promise.reject() async function rejectErrorFunc() { throw new Error('throw Error'); } rejectErrorFunc() .then((res) => console.log(res)) .catch((err)=> console.log(`ERROR ${err}`)); // => ERROR Error: throw Error // 値をthrow しても reject 扱い async function throwValueFunc() { throw 'throw Value'; } throwValueFunc() .then((res) => console.log(res)) .catch((err)=> console.log(`ERROR ${err}`)); // => ERROR throw Value // return なら返す値が Error オブジェクトでも resolve async function returnErrorFunc() { return new Error('retun Error'); } returnErrorFunc() .then((res) => console.log(`RESOLVE ${res}`)) .catch((err)=> console.log(`ERROR ${err}`)); // => RESOLVE Error: retun Error // 何も返さない関数 // 関数終了時に undefined が resolve される async function noReturnFunc() { // do't return anything } noReturnFunc() .then((res) => console.log(`RESOLVE ${res}`)) .catch((err)=> console.log(`ERROR ${err}`)); // => RESOLVE undefined
await は何をしているか
await
キーワードはasync function
内でしか使用できないawait
キーワードで呼び出した関数からPromise
が返されるまで、自身のあるasync function
の処理を止めて待機する
function waitSquareFunc(x) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(x * x); }, 500); }); } // awaitキーワードはasync内でしか使用できない async function getSquareAndAdd(x, y) { // awaitキーワードがあると値が帰ってくるまで処理が止まる const res = await waitSquareFunc(x); // Promise.resolve return res + y; } // async function を呼び出しているので Promise が返される getSquareAndAdd(2, 1) .then((res) => console.log(res)) .catch((err) => console.log(err)); // => 5 // async / await で何も返さない場合も関数が終了時の undefined が resolve される async function awaitNoReturnFunc() { // ここでPromiseが返されるまで待機して const res = await waitSquareFunc(2); // return undefined される } awaitNoReturnFunc() .then((res) => console.log(`RESOLVE ${res}`)) .catch((err)=> console.log(`ERROR ${err}`)); // => RESOLVE undefined
Promiseで書くとこんな感じ
function waitSquareFunc(x) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(x * x); }, 500); }); } function getSquareAndAddByPromise(x, y) { return waitSquareFunc(x) .then((res) => {return res + y}) .catch((err) => {throw err}); } getSquareAndAddByPromise(2, 1) .then((res) => console.log(res)) .catch((err) => console.log(err)); // => 5
エラーハンドリング
try-catchでエラーハンドリング
// isError ... true -> Promise.reject / false -> throw new Error function throwErrorFunc(isError) { return new Promise((resolve, reject) => { setTimeout(() => { try { if( isError ) { reject('Reject ERROR'); } else { // throw new Error されると catch 節に処理が流れる throw new Error('Throw new ERROR'); console.log('ココは実行されない'); resolve('NO ERROR'); } } catch(e) { console.log('> ERROR CATCH'); reject(e.message); } }, 500); }); } async function errorHandlingTry_catch(isError) { try { const res = await throwErrorFunc(isError); console.log('>> NOT IN CATCH'); return res; } catch(err) { console.log('>> TRY-CATCH ERROR'); throw err; } } // throwErrorFunc で reject error errorHandlingTry_catch(true) .then((res) => console.log(res, 'then')) .catch((err) => console.log(err, 'catch')); // => >> TRY-CATCH ERROR // => Reject ERROR catch // throwErrorFunc で throw new Error errorHandlingTry_catch(false) .then((res) => console.log(res, 'then')) .catch((err) => console.log(err, 'catch')); // => > ERROR CATCH // => >> TRY-CATCH ERROR // => Throw new ERROR catch
async function
内ではtry-catchしなくてもawait
で呼び出されている関数でPromise.reject
されると、そのままasync function
の呼び出し元にthrowされる
async function errorHandling(isError) { const res = await throwErrorFunc(isError); console.log('NOT IN CATCH'); return res; } // throwErrorFunc で reject error errorHandling(true) .then((res) => console.log(res, 'then')) .catch((err) => console.log(err, 'catch')); // => Reject ERROR catch // throwErrorFunc で throw new Error errorHandling(false) .then((res) => console.log(res, 'then')) .catch((err) => console.log(err, 'catch')); // => > ERROR CATCH // => Throw new ERROR catch
async / await で Fetch してみる
// fetch でエラーになるとPromise.rejectになるのでtry-catchは省略可 const getDataByFetchAPI = async () => { const res = await fetch(api); // fetchAPIは res.json() で Promiseを返すのでawaitを2重にする必要がある const data = await res.json(); return data; }; getDataByFetchAPI() .then((res) => console.log(res)) .catch((err) => console.log(err.message));
まとめ
遥か昔のCallback hellなExpressプロジェクトを見ていたので、JavaScriptの非同期処理すごく見やすく書けるようになったんだね。ほえーって感じです。(Promiseでも十分見やすいですよ...
async
は必ずしもawait
を使う必要はなく、Promise
を返す関数。
await
を使えばコールバックなネストになった文でなく非同期処理の待機を書けるから完結でとても便利だけど、処理を止めるという意識がないと不用意にループの中で使って処理時間が長くなってしまうとかありそうなので、await
が何をしているのか理解して並行処理したほうが良いのかとか設計するのが良さそうって思いました。
Promiseの記事を書いた時に次はasync/awaitだ!って言ったままだったので、書けてよかった。 (Promise.resolve
[参考]