my coredump

自分用の公開メモです。主にプログラムのこととか書くはず。

Promiseのスニペット

JavaScriptのPromiseが便利そうなので使い始めてたのだけど、メソッドチェインしたり入れ子にしていたら混乱してきたのでまとめる。

この数日でよく使った(そして度々間違えた)パターンのメモ。

逐次実行

こんな感じのPromiseオブエジェクトを返す関数があったとする。

function promise1() {
  console.log('promise1 args', arguments);
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      // resolveしたものがthenの引数になる
      resolve('resolve promise1');
    }, 500);
  });
}

function promise2() {
  console.log('promise2 args', arguments);
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('resolve promise2');
    }, 400);
  });
}

function promise3() {
  console.log('promise3 args', arguments);
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('resolve promise3');
    }, 300);
  });
}

単純にthenで連結すると逐次実行になる。
これは単純。

// それぞれのpromise関数には前の関数でresolveした値が引数にくる
promise1()
.then(promise2)
.then(promise3)
.then(function() {
  // promise3でresolveに与えたものが引数で渡ってくる
  console.log(arguments);
  // returnした値を次のthenのコールバックで受け取れる
  return "returm then callback";
}).then(function() {
  console.log(arguments);
});

出力。

promise1 args []
promise2 args ["resolve promise1"]
promise3 args ["resolve promise2"]
["resolve promise3"]
["returm then callback"]

別のパターン。
事前にいくつthenが必要になるか分からないときなどはこういった手間がいるかも。(以下はJavaScript Promiseの本の例そのままです)

// 以下の様なヘルパー関数を使って
// Promiseオブジェクトを返す関数の配列を
// 逐次処理することもできる
function sequenceTasks(tasks) {
  function recordValue(results, value) {
    results.push(value);
    return results;
  }
  var pushValue = recordValue.bind(null, []);
  // reduceの第2引数のpromiseオブジェクトを初期値に折りたたむ
  return tasks.reduce(function (promise, task) {
    return promise.then(task).then(pushValue);
  }, Promise.resolve());
}

// 逐次実行
sequenceTasks([promise1, promise2, promise3]);

出力。

promise1 args [undefined]
promise2 args ["resolve promise1", "resolve promise2", "resolve promise3"]
promise3 args ["resolve promise1", "resolve promise2", "resolve promise3"]

入れ子

API全体をPromise使おうとしていたんだけど、「あれ?この関数でreturnしているpromiseオブジェクトをそのまま返せば良いんだっけ?新しくnewしないといけないんだっけ?」と迷った。

結論をいうとそのまま返して良いし、返す前にthenやcatchで処理を追加してもよい。thenやcatchが返すオブジェクトも新しいpromiseオブジェクトであるため。

thenやcatchは処理を追加していくイメージっぽい。

function promiseInner() {
  console.log('promiseInner args', arguments);
  return new Promise(function(resolve, reject) {
    resolve('promiseInner');
  });
}

function promiseOuter() {
  console.log('promiseOuter args', arguments);
  // promiseのなかでもpromiseが使える
  // promise関数自体もthenも
  // 新しいPromiseオブジェクトが返るので
  // それをそのまま返せばメソッドチェインできる
  return promiseInner().then(function() {
    console.log('promiseOuter after4', arguments);
    return "return promiseInner in promiseOuter";
  });
}

promiseOuter()
.then(function() {
 console.log('afterInner', arguments);
});

出力。

promiseOuter args []
promiseInner args []
promiseOuter after4 ["promiseInner"]
afterInner ["return promiseInner in promiseOuter"]

もっと慣れたら便利っぽいので小さいところから使って慣れていこう。