2013-11-20 2 views
63

У меня есть ряд обещаний, которые нужно запускать в последовательном порядке.Как я могу выполнить массив обещаний в последовательном порядке?

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

Вызов RSVP.all будет выполнять их параллельно:

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

Но, как я могу запустить их в последовательности?

я могу вручную укладывать их, как этот

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

, но проблема в том, что количество обещаний меняется и массив обещаний строится динамически.

+0

из других ответов и нисходящих комментариев по моему, кажется, что больше людей нужно читать [rsvp README] (https://github.com/tildeio/rsvp.js?utm_source=javascriptweekly), где объясняется «Действительно потрясающий часть приходит, когда вы возвращаете обещание от первого обработчика ». Если вы этого не делаете, вы действительно упускаете выразительную силу обещаний. –

+0

Подобный вопрос, но не относящийся к структуре: http://stackoverflow.com/q/24586110/245966 –

ответ

110

Если у вас уже есть их в массиве, они уже выполняются. Если у вас есть обещание, оно уже выполняется. Это не относится к обещаниям (I.E они не похожи на C# Task s в этом отношении на метод .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

Это отличный способ построить дерево однородных обещаний, не требующих аргументов. Это в точности эквивалентно использованию указателя next_promise для сборки дерева самостоятельно, что вам нужно сделать, если набор обещаний не является однородным по отношению к аргументам и т. Д. Просто функция сокращения выполняет указатель на текущий -листный бит для вас. Вы также захотите построить дерево самостоятельно, если некоторые из ваших вещей могут произойти одновременно. В дереве обещаний ветви - это последовательности и листья параллельны. –

+0

Спасибо за ваш ответ. Вы правы, что создание обещания уже означает, что оно выполняется, поэтому мой вопрос не был правильно сформирован. Я решил решить свою проблему по-другому без обещаний. – jaaksarv

+0

Спасибо за это. Не знакомый с деталями 'сокращения', мне потребовалось некоторое время, чтобы понять, что первый« cur »- это обещание. Исходя из этого, мне удалось создать тот, который [выполняет обещания, пока один из них не удастся] (https://gist.github.com/greggman/0b6eafb335de4bbb557c). Просто добавив его здесь, если кто-то сочтет это полезным. – gman

4

Вторая попытка ответа на который я стараюсь быть более описательный:

Во-первых, некоторый требуемый фон, от RSVP README:

Действительно удивительная часть приходит, когда вы возвращаете обещание от первого обработчика ... Это позволяет сгладить вложенные обратные вызовы и является основной особенностью обещаний, которые предотвращают «правый дрейф» в программах с большим количеством асинхронного кода ,

Именно так вы выполняете обещания последовательно, возвращая позднее обещание от then обещания, которое должно было закончить перед ним.

Полезно подумать о таком наборе обещаний, как дерево, где ветви представляют собой последовательные процессы, а листья представляют собой параллельные процессы.

Процесс создания такого дерева обещаний аналогичен самой общей задаче построения других видов деревьев: поддерживать указатель или ссылку на то, где в дереве, которое вы в настоящее время добавляете ветви, и итеративно добавляете вещи.

Как отметил в своем ответе @Esailija, если у вас есть множество функций, возвращающих обещание, которые не принимают аргументы, вы можете использовать reduce, чтобы аккуратно построить дерево для вас. Если вы когда-либо реализовали сокращение для себя, вы поймете, что то, что сокращение делает за кулисами в ответе @ Esailija, поддерживает ссылку на текущее обещание (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'), чтобы гарантировать, что что-то сделанное с помощью одного обещания будет выдано до того, как будет запущено следующее обещание - мой ответ слишком сложный для слишком сложного ответа также показывает пример этого.

+2

Это намного лучше, но я чувствую, что ты все еще не согласен с темой.Это является общим для многих ответов на обещания, люди, похоже, не нашли времени, чтобы прочитать вопрос, вместо этого они просто комментируют некоторые аспекты обещаний, которые они лично понимают. Первоначальный вопрос не предполагает параллельного выполнения, даже немного, и он ясно показывает, что просто цепочки с помощью 'then' желательны, вы предоставили много дополнительной информации, которая скрывает ответ на заданный вопрос , –

+0

@DavidMcMullin ".... и это ясно показывает, что просто цепляется через то же самое ...", но на самом деле он утверждает, что последовательность обещаний строится динамически. Поэтому ему нужно понять, как построить дерево, даже если в этом случае это простое подмножество дерева «линейная последовательность». Вам все равно нужно построить его, сохранив ссылку на последнее обещание в цепочке и добавив новые обещания. –

+0

Когда OP сказал, что «количество обещаний меняется, и массив обещаний строится динамически», я уверен, что все это означало, что размер массива не был предопределен, и поэтому он не мог использовать простой 'Promise.resolve(). then (...). then (...) ...', а не то, что массив рос _while_ обещания выполнялись. Конечно, все это спорный вопрос в настоящее время. – JLRishe

0

Я собираюсь оставить этот ответ здесь, потому что это помогло бы мне, когда я придумал решение моей проблемы.

То, что я был после того, как был по существу mapSeries ... и я, случается, отображение экономии на множестве значений ... и я хочу результаты ...

Так вот, насколько я получил , FWIW, чтобы помочь другим поисках подобных вещей в будущем ....

(Обратите внимание, что контекст является приложением уголек)

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

с ECMAScript функций +2017 асинхронными это будет сделано, как это:

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

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

Вы можете использовать BabelJS использовать асинхронную функцию в настоящее время

0

Я была аналогичная проблема, и я сделал рекурсивную функцию, которая работает функции по одному последовательно.

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

Все нужно решить, что это 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); 
    }); 
} 
3

ES7 путь в 2017.

<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> 

Это будет выполнять указанные функции последовательно (один за другим), а не параллельно. Параметр promises представляет собой массив функций, которые возвращают Promise.

Plunker пример с указанным кодом: http://plnkr.co/edit/UP0rhD?p=preview

0

Я написал библиотеку для этого, может быть, это может помочь вам https://github.com/LvChengbin/sequence

вы можете установить его с НПМ:

npm i @lvchengbin/sequence --save 

Тогда вам может реализовать это следующим образом:

Sequence.all([ 
    () => promise1, 
    () => promise2, 
    () => promise3 
]).then(results => { 
    // here you will get all results of the promises in the list 
}).catch(results => { 
    // any of promises failed, and you can get all results of them 
}); 

Если вы хотите, чтобы выполнить все шаги в списке независимо от того, если какой-либо из них удалось или не удалось, вы можете использовать Sequence.chain

Sequence.all([ 
    () => promise1, 
    () => promise2, 
    () => promise3 
]).then(results => { 
    // here you will get all results of the promises in the list, and you can know which one has succeeded and which one has failed with 
    if(results[ 0 ].status === Sequence.SUCCEEDED) { 
     console.log(results[ 0 ].value); 
    } 
}) 

Вы также можете создать пустую последовательность и добавить шаги позже.

const sequence = new Sequence(); 

sequence.append(promise1); 
sequence.append([ promise1, promise2 ]); 
sequence.on('success', (result, index, results) => { 
    // this function will execute for each item succeeded. 
}); 
sequence.on('failed', (result, index, results) => { 
    // this function will execute for each item failed. 
}); 

sequence.on('end',() => { 
    // this function will be executed after all steps in the sequence finished. but you can also append more steps later. 
}); 

Если вам нужно, вы можете указать interval для выполнения каждого шага, как это:

Sequence.all([], 1000); 

Sequence.chain([], 1000); 

Sequence.any([], 1000); 

new Sequence([], { interval : 1000 }); 

Все выше код будет выполняться каждый шаг в последовательности с 1-секундным интервалом.

Смежные вопросы