とあるエンジニアの作業ブログ

JavaScript

JavaScriptのPromise, async, awaitの使い方まとめ

投稿日:

久々にNode.jsを書いたらPromise、async、awaitの使い方をすっかり忘れており思い出すのに結構時間がかかったためちゃんとアウトプットして忘れないようにします。
ついでにNode.jsからshellを同期的に実行する処理を作ったので、ユースケースとして載せときます。

Promise, async, awaitとは

Promise

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

MDN web docs

ちょっと意味がわかりません。。。
正確な定義はよくわかんないですが、要は非同期処理を自在に操作するためのオブジェクトとその処理パターンだと理解してます。
例)

  • 順次処理

     
  • 並列処理

     
  • エラーハンドリング
async

async function宣言は、 AsyncFunctionオブジェクトを返す非同期関数を定義します。非同期関数は非同期でイベントループを介して実行され、暗黙的にPromiseを返します。

MDN web docs

例によって意味がわかりませんが、Promiseよりはまだ理解できそうな気がします。

  • async function ()で非同期関数を宣言できる
  • 非同期関数は必ずPromiseを返す
await

await演算子は、async functionによってPromiseが返されるのを待機するために使用します。

MDN web docs

async functionで定義された非同期関数からPromiseが返されるのを待ってくれる演算子です。これを使うと非同期関数の順次処理がsimpleに記述できる。

Promiseの使い方 ※all()とかrace()には触れません

Promiseを返す処理の作り方

なにはともあれPromiseは返す処理を作らないと話は始まりません。
関数内でnew Promise()をreturnするというのが基本の型になります。

resolve()とreject()

resolve()
Promise処理の中でresolve(引数)を使うことで引数に指定された値をPromiseに変換して返す
reject()
resolveのエラー情報版

 
つまりresoleve()はPromise処理の中で正常系の値をreturnする、
reject()はPromise処理の中で例外をreturnすると覚えておけばOK。

Promiseの戻り値を操作する

正常処理

【実行結果】

直列に処理(メソッドチェーン)

myPromise → myPromise2の順で処理したい場合。

【実行結果】

エラーハンドリング

エラーの場合はreject()で返したエラーをcatch()で受け取る。(then()では受け取れないので注意)

【実行結果】

asyncの使い方

Promiseと比較して考えるとわかりやすい。
asyncはawaitと組み合わせて初めて意味があると思う。

asyncの基本的な使い方

【Promiseで書いた場合】

【Asyncで書いた場合】
return new Promiseを書かずともPromiseを返す。

Async関数の中で明示的にPromiseを返すこともできる。

メソッドチェーンやエラーハンドリングの仕方も基本的にPromiseと同様。

awaitの使い方

async関数の中ではawaitを宣言できる。こうすることで、非同期関数の中で別の非同期関数を呼び出した時に、呼び出した非同期関数の結果を待つことができる。(つまり処理に順序性をつけ同期的に処理できる)
async関数以外の場所(トップレベルなど)でawaitを宣言するとエラーになる。

awaitの基本的な使い方

直列に処理(メソッドチェーン)

【実行結果】

この順序性はたとえsetTimeout()などを使っていても制御可能(ただし直列に処理するので時間がかかるようになる)。

【実行結果】

エラーハンドリング

awaitを使う場合エラーハンドリングはtry { ... } catch { ... }で書く。(catch()を使って書く方法もあるが割愛)

実行結果

【ユースケース】 async, awaitを使ってNode.jsからOSコマンドを同期的に実行する

npmのchild_process.execというモジュールを使ってNode.jsから子プロセスを生成してOSコマンドを実行することができます。
しかし、このexecは非同期処理なのでなにも考えずに実行するとOSコマンドの処理結果を待たずにNode側の処理が終了するなんてことになります。
async, awaitを使ってOSコマンドの実行結果を待ち受けてNode側の処理を完了させます。
(execSyncというモジュールがあることにはこの際目をつぶってください。execの方が標準エラーの扱いが直感的に理解しやすかったのでこっちを使ってます)

async, awaitを使わずに実行

【実行結果】

sleep 3のせいでconsole.log('Node.js処理終了。');が先に実行されてしまいます。

async, awaitを使って実行

shell(cmd)というOSコマンドを実行する非同期関数と、それを呼び出すmain()というasync関数を作り、main()を実行するという流れです。
main()の中ではawaitを使ってOSコマンドとNode.js側の処理の順序を制御しています。

【実行結果】

OSコマンドの結果をawaitで待ち受けてからconsole.log('Node.js処理終了。');が実行されていることがわかります。

-JavaScript
-

執筆者:


comment

メールアドレスが公開されることはありません。

関連記事

GulpでWordPressのCI 〜実装編〜

gulpを使ったWordPressのテーマファイルCIの実装編です。 設計編はこちらを参照ください。 目次 やりたいこと・動作環境・前提 やりたいこと・環境・バージョン ディレクトリ構成 利用モジュー …

GulpでWordPressのCI 〜設計編〜

WordPressのテーマファイルをローカルで開発してgithubにpush、そのままサーバーまでデプロイするCIを実現しようと考えています。自分の環境とか踏まえてまずはどうやって実現するか設計しまし …