await
Promiseを利用した非同期処理をより簡単に書ける構文としてasync/awaitが存在します。
この構文を利用することで、非同期処理をより同期処理と同じような文脈で書くことができるようになります。
async/awaitは基本セットで使いますが、本ページではawaitを主に取り上げます。
await
awaitはPromiseの値が解決されるまで実行を待機して、解決された値を返します。
awaitの注意点として基本的にawaitはasync関数の中でしか使えません。
ts// 1秒後に値を返すfunctionrequest ():Promise <string> {return newPromise ((resolve ) => {setTimeout (() => {resolve ("hello");}, 1000);});}// この書き方はできない// const result = await request();// console.log(result);async functionmain () {constresult = awaitrequest ();console .log (result );// @log: "hello"}main ();
ts// 1秒後に値を返すfunctionrequest ():Promise <string> {return newPromise ((resolve ) => {setTimeout (() => {resolve ("hello");}, 1000);});}// この書き方はできない// const result = await request();// console.log(result);async functionmain () {constresult = awaitrequest ();console .log (result );// @log: "hello"}main ();
この例ではawait request()の行でrequest()がPromiseを解決するまで1秒待機し、コンソールに"hello"と表示します。
async/awaitで書き直す
最後にみっつのAPI呼び出しのコードをasync/awaitを利用して書き直してみます。
このようにasync/awaitを利用することで、非同期の処理を同期処理のようにスッキリ書くことができるようになります。
ts// 非同期でAPIにリクエストを投げて値を取得する処理functionrequest1 ():Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (1);}, 1000);});}// 受け取った値を別のAPIにリクエストを投げて値を取得する処理functionrequest2 (result1 : number):Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (result1 + 1);}, 1000);});}// 受け取った値を別のAPIにリクエストを投げて値を取得する処理functionrequest3 (result2 : number):Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (result2 + 2);}, 1000);});}async functionmain () {constresult1 = awaitrequest1 ();constresult2 = awaitrequest2 (result1 );constresult3 = awaitrequest3 (result2 );console .log (result3 );// @log: 4}main ();
ts// 非同期でAPIにリクエストを投げて値を取得する処理functionrequest1 ():Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (1);}, 1000);});}// 受け取った値を別のAPIにリクエストを投げて値を取得する処理functionrequest2 (result1 : number):Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (result1 + 1);}, 1000);});}// 受け取った値を別のAPIにリクエストを投げて値を取得する処理functionrequest3 (result2 : number):Promise <number> {return newPromise ((resolve ) => {setTimeout (() => {resolve (result2 + 2);}, 1000);});}async functionmain () {constresult1 = awaitrequest1 ();constresult2 = awaitrequest2 (result1 );constresult3 = awaitrequest3 (result2 );console .log (result3 );// @log: 4}main ();
Promiseを直接awaitする
関数を作らずにPromiseを直接awaitすることもできます。
tsasync functionmain () {// 1秒後に値を返すawait newPromise ((resolve ) => {setTimeout (() =>resolve , 1000);});}
tsasync functionmain () {// 1秒後に値を返すawait newPromise ((resolve ) => {setTimeout (() =>resolve , 1000);});}
async関数をawaitする
async関数をawaitすることもできます。
tsasync functionrequest ():Promise <string> {return "hello";}async functionmain () {constresult = awaitrequest ();console .log (result );// @log: "hello"}
tsasync functionrequest ():Promise <string> {return "hello";}async functionmain () {constresult = awaitrequest ();console .log (result );// @log: "hello"}
awaitしたときの型注釈
Promise, async関数の戻り値の型注釈はPromise<T>のTになります。
tsasync functionrequest ():Promise <string> {return "hello";}async functionmain () {constresult : string = awaitrequest ();// stringになるconsole .log (result );// @log: "hello"}
tsasync functionrequest ():Promise <string> {return "hello";}async functionmain () {constresult : string = awaitrequest ();// stringになるconsole .log (result );// @log: "hello"}
then-catchをtry-catchに書き換える
Promiseのthenとcatchをtry-catchに書き換えることができます。次のmain2関数はmain1関数をtry-catchで書き換えたものです。
tsasync functionrequest ():Promise <string> {return "hello";}functionmain1 () {request ().then ((result : string) => {console .log (result );// @log: "hello"}).catch ((error : unknown) => {console .log (error );});}async functionmain2 () {try {constresult : string = awaitrequest ();console .log (result );// @log: "hello"} catch (error : unknown) {console .log (error );}}
tsasync functionrequest ():Promise <string> {return "hello";}functionmain1 () {request ().then ((result : string) => {console .log (result );// @log: "hello"}).catch ((error : unknown) => {console .log (error );});}async functionmain2 () {try {constresult : string = awaitrequest ();console .log (result );// @log: "hello"} catch (error : unknown) {console .log (error );}}
拒否されたPromiseをreturnするとき
returnの前にawaitする
拒否されたPromiseをreturnする前にawaitしたときは、その関数内で例外が発生します。
tsasync functionrequest ():Promise <unknown> {throw newError ("error");}async functionmain ():Promise <unknown> {try {// return await とすることでcatchで例外を捕捉できるreturn awaitrequest ();} catch {console .log ("error");// @log: error} finally {console .log ("finally");// @log: finally}}main ().then (() => {console .log ("then");// @log: then}).catch (() => {console .log ("catch");});
tsasync functionrequest ():Promise <unknown> {throw newError ("error");}async functionmain ():Promise <unknown> {try {// return await とすることでcatchで例外を捕捉できるreturn awaitrequest ();} catch {console .log ("error");// @log: error} finally {console .log ("finally");// @log: finally}}main ().then (() => {console .log ("then");// @log: then}).catch (() => {console .log ("catch");});
このような例であれば表示されるものはerrorとfinally、そしてthenが表示されます。
returnの前にawaitしない (ただreturnする)
拒否されたPromiseをそのまま関数の戻り値にしてしまうと拒否されたまま呼び出し元に戻されます。
tsfunctionrequest ():Promise <unknown> {throw newError ("error");}// try -> finally -> return -> catch()async functionmain ():Promise <unknown> {try {returnrequest ();} catch {console .log ("error");} finally {console .log ("finally");// @log: finally}}main ().then (() => {console .log ("then");}).catch (() => {console .log ("catch");// @log: catch});
tsfunctionrequest ():Promise <unknown> {throw newError ("error");}// try -> finally -> return -> catch()async functionmain ():Promise <unknown> {try {returnrequest ();} catch {console .log ("error");} finally {console .log ("finally");// @log: finally}}main ().then (() => {console .log ("then");}).catch (() => {console .log ("catch");// @log: catch});
このような例であれば表示されるものはfinallyとcatchが表示されます。