Исходя из заголовка вопроса «Разрешать обещания одно за другим (т.е. по порядку)?», Мы можем понять, что ФП больше заинтересован в последовательной обработке обещаний по расчету, чем в последовательных вызовах как таковых .
Этот ответ предлагается:
- продемонстрировать, что последовательные вызовы не нужны для последовательной обработки ответов.
- показать жизнеспособные альтернативные шаблоны посетителям этой страницы - включая ОП, если он все еще заинтересован в течение года.
- несмотря на утверждение ОП о том, что он не хочет совершать вызовы одновременно, что может действительно иметь место, но в равной степени может быть допущением, основанным на желании последовательной обработки ответов, как следует из названия.
Если одновременные вызовы действительно нежелательны, см. Ответ Бенджамина Грюнбаума, который полностью охватывает последовательные вызовы (и т. Д.).
Однако, если вы заинтересованы (для повышения производительности) в шаблонах, которые допускают одновременные вызовы с последующей последовательной обработкой ответов, тогда, пожалуйста, продолжайте читать.
Соблазнительно думать, что вы должны использовать Promise.all(arr.map(fn)).then(fn)
(как я это делал много раз) или необычный сахар в Promise lib (особенно Bluebird), однако (с учетом этой статьи ) arr.map(fn).reduce(fn)
шаблон будет работать с преимуществами, которые он дает :
- работает с любым обещанием lib - даже с предварительно совместимыми версиями jQuery - только
.then()
используются .
- обеспечивает гибкость, позволяющую пропускать ошибки или остановки при ошибке, в зависимости от того, что вы хотите с однострочным модом.
Вот оно, написано для Q
.
var readFiles = function(files) {
return files.map(readFile) //Make calls in parallel.
.reduce(function(sequence, filePromise) {
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
Примечание: только этот один фрагмент Q()
специфичен для Q. Для jQuery необходимо убедиться, что readFile () возвращает обещание jQuery. С A + libs, иностранные обещания будут ассимилированы.
Ключевой момент здесь является сокращением в sequence
обещании, что последовательности обработки из readFile
обещаний , но не их создание.
И как только вы это освоите, может быть, это немного ошеломляет, когда вы понимаете, что .map()
сцена на самом деле не нужна! Вся работа, параллельные вызовы плюс последовательная обработка в правильном порядке, может быть выполнена в reduce()
одиночку, плюс дополнительное преимущество дополнительной гибкости для:
- Преобразуйте из параллельных асинхронных вызовов в последовательные асинхронные вызовы, просто переместив одну строку - потенциально полезно во время разработки.
Вот и Q
снова.
var readFiles = function(files) {
return files.reduce(function(sequence, f) {
var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
Это основной шаблон. Если вы хотите также передать данные (например, файлы или их преобразование) вызывающей стороне, вам понадобится мягкий вариант.