LoginSignup
5
2

More than 1 year has passed since last update.

[js] 非同期処理のdone, failを卒業する

Posted at

非同期処理を普段どう書いていますか?
僕はdone-failを用いて書いています。実はこの書き方保守性の面であまり良くない。
*jqueryを使用

$.ajax({
   url: 'https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060'
 })
 .done(function(res) { 
   console.log(res);
 })
 .fail(function(res){ 
   console.log(res);
 });
// =>
// {
//   'message': null,
//   'results': [{ ... }],
//   'status': 200
// }

*以下ajaxの中は簡略化します。

問題1. errorキャッチができない

doneの中でエラーが出たときにfailがキャッチしそうですが実はキャッチできません。

もしdoneの中でエラーハンドリングを行いたい場合はtry-catchを毎回書く必要があります。
さらにtry-catchでネストが深くなるのも嬉しくない。

$.ajax()
 .done(function(res) { 
   throw 'error';
   console.log(res);
 })
 .fail(function(e) { 
   // キャッチできない
   console.log(e);
 });

// Uncaught error 

問題2. コールバック地獄

例えば「ajax通信を複数使いたいが同期的に行いたい時」や「スコープ外の変数へのアクセスのためにコールバックを用いたい時」にネストを深くするスパイラルから抜け出せなくなります。

所謂コールバック地獄。

function successFunc(data) {
  // ...
}

function fetch(callback) {
  $.ajax()
  .done(function(res1) { 
    $.ajax()
    .done(function(res2) { 
      // 終了時の処理
      callback(res2);
     })
    .fail(function(res) { ... }) 
   })
   .fail(function(res) { ... })
})

fetch(successFunc)

解決策

コールバックを防ぐ手段としてPromiseで制御する方法があります。
Promiseに続くthenは同期的に処理を行うことを示しています。したがってコールバック地獄から抜け出せる。また、then-catchを用いることでエラーハンドリングも可能。

少し今風の書き方に変えてこんな感じで書ける。

const fetch = () => {
  return new Promise((resolve, reject) => {
    $.ajax()
    .done(res => {
      // ...
      resolve(res)
    })
    .fail(res => reject(res));
  });
}

// then-catch
fetch().then(res => ... ).catch(e => )

さらにjQueryの$.ajaxはPromiseを内包したDeferredオブジェクトが返ってくるので、より簡潔に書ける。
参考:爆速でわかるjQuery.Deferred超入門

const fetch = () => {
  return $.ajax();
}

fetch().then(res => {
    // ...
    return var
  }).then(var => {
    // ...
    throw 'error'
  }).catch(e => console.log(e))
// => error

ここで注目すべきところはdone-failは連結できないがthenは連結できるというところ。

理由はthenがPromiseオブジェクトを返すから。
参考:Promiseチェーン

async/await

さてPromiseオブジェクトを返しthen-catchを使うことでdone-failを卒業できそうな気がします。

ただ毎回resolve,rejectを書くのは忘れてしまいそうですしthenの見通しが少し悪い気もします。

もっと直感的な書き方はできないのか。。。

ありました!
その名もasync-await

asyncはPromiseを返し、resolveやrejectを明示的に使わずthenチェーンがかけます。
また、asyncの中でawaitをつけた関数がある場合、awaitの関数が終了するまで後続の処理は実行されません。

const fetch = () => {
  return $.ajax(...)
}

const timer2Seconds = res => {
  return new Promise(resolve => {
    setTimeout( () => {
      console.log(res);
      resolve('timeout');
    }, 2000);
  });
}

const main = async () => {
  const res = await fetch();
  const timer = await timer2Seconds(res);
  return timer
}

main().then(message => {throw message}).catch(e => console.log(e));
// => timeout

メリットはこちらの記事がわかりやすいです。
JavaScriptのasync/awaitがPromiseよりもっと良い

5
2
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
5
2