Promiseについてなんかいい感じに書くその2


またしても間に合わなかったどころか次の日のポストより遅かった・・・すみません


この記事ですが、前編の記事を読んでいて、ある程度理解していることが前提となります。
もし良かったら前編の記事もお読みください。

前回 => promiseについてなんかいい感じに書く


さて、前編ではpromiseについて話し、糞みたいなコールバック地獄がキレイスッキリ!になりましたね。
後編ではpromiseの便利な機能と、更に便利に使う方法について書いていきます。

allとrace

まずは便利な機能の話からしましょう。
既に前編の説明で十分便利だったPromiseですが、確かに連続した処理をするときには便利ですが、同時に実行したい! という時はちょっと不便です。

ひとつサンプルを出してみます。
前回はあるAPIから取得したデータで次のAPIを叩いて...といった例でしたが、今回は複数のAPIを同時に叩いてその結果をまとめる処理・・・ですが、これを実現するのは既に難しいです。
とりあえず無理やりコードにしてみます。

let data1, data2, data3;

getFromAPI(request1)
.then((data) => {data1 = data;})
.catch((error) => {console.error(error)});
getFromOtherAPI(request2)
.then((data) => {data2 = data;});
.catch((error) => {console.error(error)});
getFromSomeOtherAPI(request3)
.then((data) => {data3 = data;});
.catch((error) => {console.error(error)});

const check = setInterval(() => {
  if (data1 !== undefined && data2 !== undefined && data3 !== undefined) {
    viewRendering(data1, data2, data3);
    clearInterval(check);
  }
}, 100);

やりようは色々あるでしょうが、こんな感じでしょうか。。。もう誰が見てもわかる酷さですね。

さて、こんな例を出したからにはもちろんこれからする説明はこれを解決するものです。
まずはPromise.all()

これは引数にpromiseの配列を与えることで、

配列内のpromiseを同時に実行し、全てが完了するか、一つでも失敗した場合、thenまたはcatchに移る

メソッドです。

先程のサンプルを直してみましょう。

Promise.all([getFromAPI(request), getFromOtherAPI(request), getFromSomeOtherAPI(request)])
.then((result) => {viewRendering(result[0], result[1], result[2])})
.catch((error) => {console.error(error)});

たった三行に収まりました!

では次はPromiseのもう一つのメソッドPromise.raceです。

こちらは引数は同じくpromiseの配列を与えるのですが、処理に関しては違い

与えたpromiseを同時に実行し、一番最初に返ってきた完了または失敗をthen,catchに渡す

メソッドになります。

こちらは上で上げたサンプルでは使えないので、別の例を考えてみます。
実用的な例だと・・・複数のAPIを同時に叩き、一番最初に返ってきた結果を返す とかでしょうか。
同じ結果を返すAPIのうち一番速度が早かったものを使用する例なんかも考えられますね。
サンプルコードを・・・と思いましたが殆どallと変わりませんでした・・・
一応乗せておきます。

Promise.race([getFromAPI(request), getFromOtherAPI(request), getFromSomeOtherAPI(request)])
.then((result) => {viewRendering(result)})
.catch((error) => {console.error(error)});

といったところでPromiseの便利なメソッドは終わりです。

はい。終わりです。 Promise自体にはメソッドはこれしかありません。

では便利な機能はこれで終わりか、いえいえ違います。

async/await

ここまでPromiseについて色々書いてきた上、タイトルもPromiseについてなんかいい感じに書くですが、いよいよ本題です。 

実はここまでは全て前座です。

async

javascriptの関数定義時にasyncをつけて関数を定義すると、結果をpromiseにして返す関数にすることができます。
コードにしてみましょう。

async function testFucntion1() {
  return "test1";
}

const testFunction2 = async () => {
  return "test2";
}

const testFunction3 = async () => {
  throw new Error("error");
}

testFunction1()
.then((result) => {console.log(result)}); // true

testFunction2()
.then((result) => {console.log(result)}); // true

testFunction3()
.catch((err) => {console.error(err)}); // Error: err

といった感じです。
いちいちpromiseを書かなくても勝手にPromise関数にしてくれる。
確かに便利ですが、asyncは基本的にawaitとセットで使います。

await

さらっと概要を説明すると、async関数内でpromiseを返す関数を呼び出す時にawaitをつけるとその関数が同期的に実行されるようになります。

こっちは恐らく言葉だとちょっと解りづらいと思うので、コードベースで説明します。

(async () => {
  const data = await getFromAPI(request);
  console.log(data);
})()

全体のサンプルコードとしてはこんな感じです。
では一行ずつ説明します。

(async () => {
...
}())

awaitはasync関数外では使えず、エラーになってしまうので、ここでは即時実行関数を作ってasync関数にしています。

const data = await getFromAPI(request);

これまでのpromiseでは.then, .catchで結果を受け取っていましたが、awaitで同期化した関数では、通常の関数と同じように結果を受け取ることができます。

console.log(data);

同期化していて、getFromAPI内の処理が終わった段階で次の処理に移るため、promiseの際と違って普通に次の行で呼び出しても問題ありません。

Promise, async/awaitをうまく使うことで、もうコールバック地獄に悩まされることはないでしょう。

 あとがき

とまぁ、今回はそもそも書きはじめすら投稿予定日に間に合わないという前回以上のポカをやらかしてますが、書きたかったPromiseについてはおおよそまとめられたので良しとします。

それでは、12/10(2日も過ぎたけど)のMake IT Advent Calendar 2018、お役に立ちましたでしょうか?

ここまでご愛読頂き、ありがとうございました!