web-dev-qa-db-ja.com

約束の配列を順番に実行するにはどうすればよいですか?

順番に実行する必要があるプロミスの配列があります。

var promises = [promise1, promise2, ..., promiseN];

RSVP.allを呼び出すと、それらが並行して実行されます。

RSVP.all(promises).then(...); 

しかし、それらを順番に実行するにはどうすればよいですか?

このように手動でスタックできます

RSVP.resolve()
    .then(promise1)
    .then(promise2)
    ...
    .then(promiseN)
    .then(...);

しかし問題は、約束の数が異なり、約束の配列が動的に構築されることです。

74
jaaksarv

すでに配列に含まれている場合は、すでに実行されています。約束があれば、すでに実行されています。これはプロミスの懸念ではありません(つまり、.Start()メソッドに関するC#Tasksとは異なります)。 .allは何も実行せず、単にpromiseを返します。

関数が返す関数の配列がある場合:

var tasks = [fn1, fn2, fn3...];

tasks.reduce(function(cur, next) {
    return cur.then(next);
}, RSVP.resolve()).then(function() {
    //all executed
});

または値:

var idsToDelete = [1,2,3];

idsToDelete.reduce(function(cur, next) {
    return cur.then(function() {
        return http.post("/delete.php?id=" + next);
    });
}, RSVP.resolve()).then(function() {
    //all executed
});
126
Esailija

ECMAScript 2017の非同期関数を使用すると、次のようになります。

async function executeSequentially() {
    const tasks = [fn1, fn2, fn3]

    for (const fn of tasks) {
        await fn()
    }
}

BabelJS を使用して、今すぐ非同期関数を使用できます

16
ujeenator

2017年のES7の方法。

  <script>
  var funcs = [
    _ => new Promise(resolve => setTimeout(_ => resolve("1"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("2"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("3"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("4"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("5"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("6"), 1000)),
    _ => new Promise(resolve => setTimeout(_ => resolve("7"), 1000))
  ];
  async function runPromisesInSequence(promises) {
    for (let promise of promises) {
      console.log(await promise());
    }
  }
  </script>
  <button onClick="runPromisesInSequence(funcs)">Do the thing</button>

これは、与えられた関数を並列ではなく、順番に(1つずつ)実行します。パラメータpromisesは、Promiseを返す関数の配列です。

上記のコードを使用したプランカーの例: http://plnkr.co/edit/UP0rhD?p=preview

6
allenhwkim

私がもっと説明しようとする答えの2回目の試み:

まず、 RSVP README からのいくつかの必要な背景:

最初のハンドラーからプロミスを返すときに本当に素晴らしい部分があります...これはネストされたコールバックをフラット化することを可能にし、多くの非同期コードを持つプログラムで「右方向のドリフト」を防ぐプロミスの主な機能です。

これは、前に終了するプロミスのthenから後のプロミスを返すことにより、プロミスをシーケンシャルにする方法です。

そのような一連のプロミスをツリーと考えると便利です。ツリーは、ブランチが順次プロセスを表し、リーフが並行プロセスを表すものです。

そのような約束のツリーを構築するプロセスは、他の種類のツリーを構築する非常に一般的なタスクに似ています:現在ブランチを追加しているツリーの場所へのポインターまたは参照を維持し、繰り返し追加します。

@Esailijaが彼の答えで指摘したように、引数をとらないプロミスを返す関数の配列がある場合は、reduceを使用してツリーをきちんと構築できます。自分でreduceを実装したことがある場合、@ Esailijaの答えで舞台裏でreduceが行っていることは、現在のプロミス(cur)への参照を維持し、各プロミスが次のプロミスを返すことですthen

均質なニース配列(引数を取る/返す)が関数を返すことを約束していない場合、または単純な線形シーケンスよりも複雑な構造が必要な場合は、以下を維持することで自分で約束のツリーを構築できます新しいプロミスを追加するプロミスツリー内の位置への参照:

var root_promise = current_promise = Ember.Deferred.create(); 
// you can also just use your first real promise as the root; the advantage of  
// using an empty one is in the case where the process of BUILDING your tree of 
// promises is also asynchronous and you need to make sure it is built first 
// before starting it

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

current_promise = current_promise.then(function(){
  return // ...something that returns a promise...;
});

// etc.

root_promise.resolve();

RSVP.allを使用して複数の「リーブ」をプロミス「ブランチ」に追加することにより、同時プロセスと順次プロセスの組み合わせを構築できます。あまりにも複雑であると投票された私の答えは、その例を示しています。

また、Ember.run.scheduleOnce( 'afterRender')を使用して、1つのプロミスで行われた何かが次のプロミスが実行される前にレンダリングされることを確認できます。

4

私はこの答えをここに残します。これは、私の問題の解決策を探しに来たときに助けてくれたからです。

私が後にしたことは本質的にmapSeriesでした...そして、私はたまたま値のセットを保存してマッピングしています...そして結果が欲しいです...

だから、ここで私が得た限り、FWIWは、他の人が将来同じようなものを探しているのを助けるために...

(コンテキストはemberアプリ)であることに注意してください

App = Ember.Application.create();

App.Router.map(function () {
    // put your routes here
});

App.IndexRoute = Ember.Route.extend({
    model: function () {
            var block1 = Em.Object.create({save: function() {
                return Em.RSVP.resolve("hello");
            }});
    var block2 = Em.Object.create({save: function() {
            return Em.RSVP.resolve("this");
        }});
    var block3 = Em.Object.create({save: function() {
        return Em.RSVP.resolve("is in sequence");
    }});

    var values = [block1, block2, block3];

    // want to sequentially iterate over each, use reduce, build an array of results similarly to map...

    var x = values.reduce(function(memo, current) {
        var last;
        if(memo.length < 1) {
            last = current.save();
        } else {
            last = memo[memo.length - 1];
        }
        return memo.concat(last.then(function(results) {
            return current.save();
        }));
    }, []);

    return Ember.RSVP.all(x);
    }
});
0
Julian Leviston

解決するにはすべてがforループです:)

var promises = [a,b,c];
var chain;

for(let i in promises){
  if(chain) chain = chain.then(promises[i]);
  if(!chain) chain = promises[i]();
}

function a(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve A');
      resolve();
    },1000);
  });
}
function b(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve B');
      resolve();
    },500);
  });
}
function c(){
  return new Promise((resolve)=>{
    setTimeout(function(){
      console.log('resolve C');
      resolve();
    },100);
  });
}
0
Paweł

同様の問題があり、関数を1つずつ順番に実行する再帰関数を作成しました。

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function() {
      return executeSequentially(tasks);
    });
  }

  return Promise.resolve();  
};

これらの機能から出力を収集する必要がある場合:

var tasks = [fn1, fn2, fn3];

var executeSequentially = function(tasks) {
  if (tasks && tasks.length > 0) {
    var task = tasks.shift();

    return task().then(function(output) {
      return executeSequentially(tasks).then(function(outputs) {
        outputs.Push(output);

        return Promise.resolve(outputs);  
      });
    });
  }

  return Promise.resolve([]);
};
0
mrded