3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

#おおまかな非同期説明

##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になる

上記はぬるい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 がとても便利ってこと

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?