Help us understand the problem. What is going on with this article?

年末休みにnodeでAPIサーバを勉強した復習〜part4 async/awaitの説明

More than 1 year has passed since last update.

おおまかな非同期説明

callback hell

nodeは非同期フレームワークである
非同期とはなにかというのを全て説明するのは面倒なので調べて欲しいが
I/Oの時間のかかる処理を待たずに遅延実行する事によりCPUを有効に使う方法だ
I/Oの処理が終わったら実行を継続するため、次の処理をコールバック関数で渡す事になる
例えば

var fs = require('fs');
fs.readFile('hoge.txt', 'utf8', function (err, text) {
  if(err){
    // エラー処理
  else{
    // 正常処理
  }
...
});

のように、引数の最後にあるのが読み込み終了時のコールバックだ
これで、I/Oを待たずCPUは次の処理を行い、I/Oから戻ってきたらコールバックが処理され
非同期に実行できるようになった

しかしこのままでは callback hellになる

http://callbackhell.com/

上記はぬるいHellだが、私はnode初期の頃に恐ろしい地獄を作ったこともある
callback hellは個人的には楽しいが、色々な問題に直面する

実行順がぱっと見でわかりにくい
例外の扱いが難しい
デバッグが難しい

などがあるため、promise/futureや async/awaitを使うべきだ

function hogeAsync(msg, callback){
  console.log(msg);

  if(callback!=null) callback();
}

hogeAsync("1", hogeAsync("2", hogeAsync("3")));

(console.logはそんな時間かからないが サンプルなのでそんなかんじで)
期待する結果は?
答えは 321 だ!
更にエラー処理等が入ると実行順序はますますわかりにくくなる

promise/future

昔のAWS Lambdaでは async/awaitが使えなかったはずで
その時にpromise/futureを使っていた

promise/futureでは、非同期処理をメソッドチェーンで同期的につなげることができる
callback hellと違い、実行順もイメージ通りになる

function hogeAsync(msg){
  const promise = new Promise((resolve, reject) => {
    console.log(msg);
    resolve();  
  });
  return promise;
}

hogeAsync("1")
.then(()=>hogeAsync("2"))
.then(()=>hogeAsync("3"))

結果は期待通り、123だ

async/await

promise/futureでも非同期処理を同期的に扱うことが出来たが
メソッドチェーンでつなぐため、非同期のネストが深くなったり
間に制御構文を入れた場合にも読みにくくなるし
例外処理が面倒だ

そこでasync/awaitを使う
実はasync/awaitは内部はpromiseである
promiseにかぶせて、より同期的に見せているだけである

async/awaitはメソッドチェーンで繋がず、文を終わらせるためネストが深くなり辛い
また、例外を全部同じところで取れるし
エラー時のスタックトレースも比較的楽になる
そのため、現時点では async/awaitがベストだと思う

promise/futureとasync/awaitの比較
https://qiita.com/Anders/items/dfcb48d8b27ceaffb443

function hogeAsync(msg){
  const promise = new Promise((resolve, reject) => {
    console.log(msg);
    resolve();  
  });
  return promise;
}

async function hoge(){
await hogeAsync("1");
await hogeAsync("2");
await hogeAsync("3");
};
hoge();

当然結果は 123

また、async属性を関数につければ、自動的にpromiseを生成しreturnするので
下記のように省略できる

async function hogeAsync(msg){
    console.log(msg);
}

(async () => {
await hogeAsync("1");
await hogeAsync("2");
await hogeAsync("3");
})();

async/awaitの説明

結局Promiseを見えなくして使ってるだけなのですよ
説明はググって

複数の非同期関数を同期させる

Promiseではallを使うと、複数の非同期関数をまとめて同期出来たが
async/awaitでも同じものを使う

Promise.all
https://qiita.com/im36-123/items/c0678a46ee0f8e44e150

expressの場合

nodeでAPIサーバに使われるExpressフレームワークはなんとPromiseに対応されていない
が、コールバック地獄は勘弁
なので 下記のように少しでも使いやすくする

expressで非同期するときのパターン
https://qiita.com/yukin01/items/1a36606439123525dc6d
https://medium.com/@Abazhenov/using-async-await-in-express-with-node-8-b8af872c0016

同じく
http://www.acuriousanimal.com/2018/02/15/express-async-middleware.html

つまり、node-async-handlerを使って、例外はnextに投げておけば、とりあえずの所はちゃんと動く

asynchell

https://qiita.com/rana_kualu/items/e6c5c0e4f60b0d18799d

async/await使っても Hellになる事ある
とりあえず、関数の依存関係は考えることと
promise.all がとても便利ってこと

YukiMiyatake
C++が喋れる ゲームプログラマ インフラ、サーバ、UNITY、ゲームエンジンが最近多いな・・ MONA: MPpuEnmqDYBCxSZyG5cBDt6UWtXczmRmkn BTC: 13JpgsF3n6K2WhjEeUuUUqS7V71gWdFx56 BCH: 18q6rfi9ynyTgynrB8tJ2eSDLPQM32RZk5
http://murasame-labo.hatenablog.com/
murasame
ゲーム、エンタメ、サーバインフラ等 少人数で技術力の高い仕事をする会社
http://murasame-lab.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away