yuw27b’s blog

技術メモと雑記

JavaScriptのPromiseとarray.reduceを合わせて使う

順番に非同期の処理をしたい要素たちを配列に入れて、各要素に対してPromiseを返す関数を実行する。

例)['a', 'b', 'c']という配列があったときに、それぞれの文字を2秒おきにコンソールに表示したい

//これだと2秒後にほぼ同時に「a」「b」「c」がコンソールに表示されるのでNG
function wait2sec (item) { //2秒後に引数itemをコンソールに表示するfunction
  return new Promise( function(resolve, reject) {
    window.setTimeout(function() {
      console.log(item);
      resolve();
    }, 2000);
  });
}

['a', 'b', 'c'].forEach(function (item) {
  wait2sec(item);
});
//2秒おきにconsole.log()される!
function wait2sec (item) { //このfunctionはさっきと同じ
  return new Promise(function (resolve, reject) {
    window.setTimeout(function () {
      console.log(item);
      resolve();
    }, 2000);
  });
}

['a', 'b', 'c'].reduce(function (prevValue, currentValue) {
  return prevValue.then(function () { ////直前の要素の処理(2秒かかる)が終わってから次の要素をwait2sec()に渡す
    return wait2sec(currentValue);
  });
}, Promise.resolve());


たまにIE8から対応とかあるのでjQueryだけでどうにかする(だいぶ無理矢理感が・・・)

function wait2sec (list, i) {
  var dfd = new $.Deferred();
  window.setTimeout(function () {
    console.log(list[i]);
    dfd.resolve(i + 1);
  }, 2000);
  return dfd.promise();
}

var list = ['a', 'b', 'c'];
var i = 0;
var prevValue = new $.Deferred().resolve(0);
for (i = 0; i < list.length; i += 1) {
  prevValue = prevValue.then( function (next_i) {
    return wait2sec(list, next_i);
  });
};


参考文献

こちらを参考にコード書きました!
JavaScript Promises: There and back again - HTML5 Rocks


Array.prototype.reduceについて

使ったことなかったですが、便利ですね!

Array.prototype.reduce() - JavaScript | MDN

reduce関数は結構有用っていうお話 - あと味

IE9以降で使えるそう。過去に書いたコードの中にも、これを使えばもう少し簡潔に書けたものがありそうな気する・・・。


使いどころ

Ajaxで、サーバ側APIからの応答を待って処理したいときにこんな感じで書いてます。
今のところ、

  • HTMLコンテンツをパーツごとに取得したいケース
  • ファイルを1つずつアップロードしたいケース

に遭遇しました。


非同期のループはまた使いそうなので、備忘録として。