4
5

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.

ES6 の Promise で非同期通信を試す(追記:RxJS でもやってみた)

Last updated at Posted at 2015-06-26

環境

Babel で ES6 のコードを ES5 に変換(Promise Babel の polyfill を利用)し、Browserifyで依存するモジュール含めてビルドしています。

このオレオレ ES 6環境は GitHub に置いてあります。
https://github.com/hkusu/es6-learning-environment

主要なライブラリのバージョンは次のとおり。
・ babelify:6.1.2
・ browserify:10.2.4

ダミーの WEB-API を用意

非同期通信といえば WEB-API だと思いますが、今回は JavaScript の setTimeout() を利用して、WEB-API を模した関数を用意しました。

コードは ES6 で書いてます。

app.es6
// Babel の現バージョンでは Polyfill が必要なので読み込み
require("babelify/polyfill");

// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
  return new Promise((resolve, reject) => {
    // 2 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success A");
      //reject(new Error("error A")); // エラー発生を模す場合
    }, 2000);
  });
};

// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
  return new Promise((resolve, reject) => {
    // 3 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success B");
      //reject(new Error("error B")); // エラー発生を模す場合
    }, 3000);
  });
};

上記では new Promise() で Promise オブジェクトを作成していますが、新しめの JQuery だと恐らく $.ajax() は Promise を返すはずなので、そのまま Ajax で WEB-API を呼び出せばよいと思います。

並列で WEB-API(ダミー) を呼び出す

WEB-API の実行順序を問わない場合です。Promise のスタティックメソッドである Promise.all() を利用します。

app.es6
// 並列で A、B を API コール(それぞれの結果は配列にマージされる)
function doParallelRequest() {
  return Promise.all([dummyHttpRequestA(), dummyHttpRequestB()]);
}

doParallelRequest().then((message) => {
  // リクエスト A、B が完了した場合
  console.log(message); // => 3秒後に [ 'return message A', 'return message B' ] が出力される
}).catch(e => {
  // リクエスト A、B のどちらかで reject が返された場合
  console.log(e);
});

直列で WEB-API(ダミー) を呼び出す

WEB-API A の結果を待ってから、WEB-API B を呼び出すパターンです。例えば後続の WEB-API を呼び出す際のパラメータを組み立てるのに、前の WEB-API のレスポンス結果が必要な場合など。

app.es6
// 直列で A → B の順番で API コール
function doSerialRequest() {
  return dummyHttpRequestA().then((message) => {
    // リクエスト A が完了した場合
    console.log(message); // => 2秒後に return message A が出力される
    return dummyHttpRequestB();
  });
}

doSerialRequest().then((message) => {
  // リクエスト B が完了した場合
  console.log(message); // => 更に3秒後に return message B が出力される
}).catch(e => {
  // リクエスト A、B のどちらかで reject が返された場合
  console.log(e);
});

補足

then の中で Promise を return した場合、その then の実行は中の Promise が resolve されるまで待機します。

この仕組みを使わない場合は、例えば直列の場合だと次のようなコードになります。

app.es6
dummyHttpRequestA().then((message) => {
  // リクエスト A が完了した場合
  console.log(message); // => 2秒後に return message A が出力される
  dummyHttpRequestB().then(() => {
    // リクエスト B が完了した場合
    console.log(message); // => 更に3秒後に return message B が出力される
  }).catch(e => {
    // リクエスト B で reject が返された場合
    console.log(e);
  })
}).catch(e => {
  // リクエスト A で reject が返された場合
  console.log(e);
});

【2015.06.29追記】RxJS を利用した場合

RxJS の導入

$ npm install --save-dev rx

動作確認

app.es6
require("babelify/polyfill");
import Rx from 'rx'

// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
  return new Promise((resolve, reject) => {
    // 2 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success A");
      //reject(new Error("error A")); // エラー発生を模す場合
    }, 2000);
  });
};

Rx.Observable.fromPromise(dummyHttpRequestA())
  .subscribe((message) => {
    console.log(message); // => 2秒後に return message A が出力される
  });

直列で WEB-API(ダミー) を呼び出す

app.es6
require("babelify/polyfill");
import Rx from 'rx'

// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
  return new Promise((resolve, reject) => {
    // 2 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success A");
      //reject(new Error("error A")); // エラー発生を模す場合
    }, 2000);
  });
};

// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
  return new Promise((resolve, reject) => {
    // 3 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success B");
      //reject(new Error("error B")); // エラー発生を模す場合
    }, 3000);
  });
};

Rx.Observable.fromPromise(dummyHttpRequestA())
  .flatMap((message) => {
    console.log(message); // => 2秒後に return message A が出力される
    return Rx.Observable.fromPromise(dummyHttpRequestB());
  })
  .subscribe((message) => {
    console.log(message); // => 3秒後に return message B が出力される
  });

Rx.Observable.fromPromise()flatMap() を利用すると、Promise な非同期通信をストリームに載せて処理することができます。別の Observable なストリームを起点にする場合は次の通り。

app.es6
require("babelify/polyfill");
import Rx from 'rx'

// ダミーの HTTP リクエスト A
var dummyHttpRequestA = () => {
  return new Promise((resolve, reject) => {
    // 2 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success A");
      //reject(new Error("error A")); // エラー発生を模す場合
    }, 2000);
  });
};

// ダミーの HTTP リクエスト B
var dummyHttpRequestB = () => {
  return new Promise((resolve, reject) => {
    // 3 秒後にメッセージを返す
    setTimeout(() => {
      resolve("success B");
      //reject(new Error("error B")); // エラー発生を模す場合
    }, 3000);
  });
};

Rx.Observable.return("foo")
  .flatMap(() => {
    return Rx.Observable.fromPromise(dummyHttpRequestA());
  })
  .flatMap((message) => {
    console.log(message); // => 2秒後に return message A が出力される
    return Rx.Observable.fromPromise(dummyHttpRequestB());
  })
  .subscribe((message) => {
    console.log(message); // => 3秒後に return message A が出力される
  });
4
5
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
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?