Как я могу выполнить массив из promises в последовательном порядке?
У меня есть массив promises, который должен выполняться в последовательном порядке.
var promises = [promise1, promise2, ..., promiseN];
Вызов RSVP.all будет выполнять их параллельно:
RSVP.all(promises).then(...);
Но как я могу запустить их в последовательности?
Я могу вручную их складывать как
RSVP.resolve()
.then(promise1)
.then(promise2)
...
.then(promiseN)
.then(...);
но проблема в том, что число promises меняется и массив из promises создается динамически.
Ответы
Ответ 1
Если у вас уже есть их в массиве, они уже выполняются. Если у вас есть обещание, то оно уже выполняется. Это не проблема promises (I.E они не похожи на С# Task
в этом отношении с помощью метода .Start()
). .all
ничего не выполняет
он просто возвращает обещание.
Если у вас есть функция возврата обещаний:
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
});
Ответ 2
С помощью асинхронных функций ECMAScript 2017 это будет сделано следующим образом:
async function executeSequentially() {
const tasks = [fn1, fn2, fn3]
for (const fn of tasks) {
await fn()
}
}
Вы можете использовать BabelJS для использования асинхронных функций сейчас
Ответ 3
Вторая попытка ответа, в которой я стараюсь быть более понятным:
Во-первых, некоторый необходимый фон, из RSVP README:
Действительно удивительная часть возникает, когда вы возвращаете обещание от первого обработчика... Это позволяет сгладить вложенные обратные вызовы и является основной особенностью promises, которая предотвращает "правый дрейф" в программах с большим количеством асинхронный код.
Именно так вы делаете promises последовательным, возвращая позднее обещание от then
обещания, которое должно было закончить до него.
Полезно подумать о таком наборе promises как о дереве, где ветки представляют собой последовательные процессы, а листья представляют собой параллельные процессы.
Процесс создания такого дерева promises аналогичен самой общей задаче построения других видов деревьев: поддерживать указатель или ссылку на то, где в дереве, в котором вы сейчас добавляете ветки, и итеративно добавлять вещи.
Как отметил в своем ответе @Esailija, если у вас есть массив функций, возвращающих обещание, которые не принимают аргументы, вы можете использовать reduce
для аккуратной сборки дерева для вас. Если вы когда-либо реализовали сокращение для себя, вы поймете, что то, что сокращение делает за кулисами в ответе @Esailija, поддерживает ссылку на текущее обещание (cur
) и каждое обещание возвращает следующее обещание в его then
.
Если у вас нет хорошего массива однородных (по отношению к аргументам, которые они принимают/возвращают) обещают возвращающие функции, или если вам нужна более сложная структура, чем простая линейная последовательность, вы можете построить дерево promises самостоятельно, сохраняя ссылку на позицию в дереве обещаний, где вы хотите добавить новый promises:
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'), чтобы гарантировать, что что-то сделанное с помощью одного обещания будет вынесено до того, как будет запущено следующее обещание - мой ответ слишком сложный для слишком сложного ответа также показывает пример из этого.
Ответ 4
ES7 в 2017 году.
<script>
var funcs = [
_ => new Promise(res => setTimeout(_ => res("1"), 1000)),
_ => new Promise(res => setTimeout(_ => res("2"), 1000)),
_ => new Promise(res => setTimeout(_ => res("3"), 1000)),
_ => new Promise(res => setTimeout(_ => res("4"), 1000)),
_ => new Promise(res => setTimeout(_ => res("5"), 1000)),
_ => new Promise(res => setTimeout(_ => res("6"), 1000)),
_ => new Promise(res => setTimeout(_ => res("7"), 1000))
];
async function runPromisesInSequence(promises) {
for (let promise of promises) {
console.log(await promise());
}
}
</script>
<button onClick="runPromisesInSequence(funcs)">Do the thing</button>
Это будет выполнять указанные функции последовательно (один за другим), а не параллельно. Параметр promises
представляет собой массив функций, возвращающих Promise
.
Пример Plunker с приведенным выше кодом: http://plnkr.co/edit/UP0rhD?p=preview
Ответ 5
Я собираюсь оставить этот ответ здесь, потому что это помогло бы мне, когда я придумал для решения моей проблемы.
То, что я был после, было по существу 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);
}
});
Ответ 6
У меня была аналогичная проблема, и я сделал рекурсивную функцию, которая последовательно запускает функции последовательно.
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([]);
};
Ответ 7
Все, что нужно решить, это цикл 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);
});
}