Рассмотрим следующий код, который читает массив файлов в последовательном / последовательном порядке. readFiles
возвращает обещание, которое разрешается только после последовательного чтения всех файлов.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) =>
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start!
});
};
Вышеприведенный код работает, но мне не нравится делать рекурсию, чтобы вещи происходили последовательно. Есть ли более простой способ переписать этот код, чтобы мне не приходилось использовать мою странную readSequential
функцию?
Первоначально я пытался использовать Promise.all
, но это привело readFile
к одновременному выполнению всех вызовов, а это не то, что я хочу:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
источник
источник
readFileSequential()
уже вернулся до вызова следующего (поскольку он асинхронный, он завершается задолго после того, как исходный вызов функции уже был возвращен).Ответы:
Обновление 2017 : я бы использовал асинхронную функцию, если среда ее поддерживает:
При желании вы можете отложить чтение файлов до тех пор, пока они вам не понадобятся, с помощью асинхронного генератора (если ваша среда это поддерживает):
Обновление: Вторая мысль - я мог бы вместо этого использовать цикл for:
Или более компактно, с уменьшением:
В других библиотеках обещаний (например, когда и Bluebird) у вас есть служебные методы для этого.
Например, Bluebird будет:
Хотя на самом деле нет никаких причин, чтобы не использовать асинхронное ожидание сегодня.
источник
Promise.resolve(Promise.resolve(15))
идентичнаPromise.resolve(15)
.Вот как я предпочитаю выполнять задачи последовательно.
источник
result = result.then(task);
Этот вопрос старый, но мы живем в мире ES6 и функционального JavaScript, поэтому давайте посмотрим, как мы можем его улучшить.
Поскольку обещания выполняются немедленно, мы не можем просто создать массив обещаний, они все будут выполняться параллельно.
Вместо этого нам нужно создать массив функций, которые возвращают обещание. Каждая функция будет выполняться последовательно, что затем запускает обещание внутри.
Мы можем решить это несколькими способами, но мой любимый способ - использовать
reduce
.Это становится немного сложнее, используя
reduce
в сочетании с обещаниями, поэтому я разбил один вкладыш на несколько меньших усваиваемых кусочков ниже.Суть этой функции заключается в использовании
reduce
начиная с начального значенияPromise.resolve([])
или обещания, содержащего пустой массив.Это обещание затем будет передано в
reduce
метод какpromise
. Это ключ к последовательному соединению каждого обещания. Следующее обещание, которое нужно выполнить, -func
и когдаthen
сгорают, результаты объединяются, и это обещание затем возвращается, выполняяreduce
цикл со следующей функцией обещания.После выполнения всех обещаний возвращаемое обещание будет содержать массив всех результатов каждого обещания.
Пример ES6 (один вкладыш)
Пример ES6 (разбитый)
Использование:
источник
Array.prototype.concat.bind(result)
это часть, которую я пропустил, пришлось вручную подталкивать к результатам, что работало, но было менее крутоconsole.log.bind(console)
утверждение в вашем последнем примере теперь обычно не нужно. В эти дни вы можете просто пройтиconsole.log
. Например.serial(funcs).then(console.log)
, Протестировано на текущих nodejs и Chrome.Promise.resolve([]).then((x) => { const data = mockApi('/data/1'); return Promise.resolve(x.concat(data)) }).then((x) => { const data = mockApi('/data/2'); return Promise.resolve(x.concat(data)); });
Promise.resolve([]).then(x => someApiCall('url1').then(r => x.concat(r))).then(x => someApiCall('url2').then(r => x.concat(r)))
и так далееЧтобы сделать это просто в ES6:
источник
files.forEach
если файлы являются массивом.for (file of files) {...}
.Promise.resolve()
чтобы создать уже решенное обещание в реальной жизни. Почему нет?Promise.resolve()
кажется чище чемnew Promise(success => success())
.Promise.resolve();
в своем коде.return sequence;
я положилsequence.then(() => { do stuff });
Простая утилита для стандартного обещания Node.js:
ОБНОВИТЬ
items-обещание - готовый пакет NPM, делающий то же самое.
источник
Мне пришлось выполнить много последовательных задач и использовать эти ответы, чтобы создать функцию, которая позаботилась бы о выполнении любой последовательной задачи ...
Функция принимает 2 аргумента + 1 необязательный. Первый аргумент - это массив, над которым мы будем работать. Второй аргумент - это сама задача, функция, которая возвращает обещание, следующая задача будет запущена только после разрешения этого обещания. Третий аргумент - это обратный вызов, который запускается, когда все задачи выполнены. Если обратного вызова не передается, то функция возвращает обещание, которое она создала, чтобы мы могли обработать конец.
Вот пример использования:
Надеюсь, это сэкономит кому-то время ...
источник
Самым хорошим решением, которое я смог выяснить, было
bluebird
обещание. Вы можете просто сделать то,Promise.resolve(files).each(fs.readFileAsync);
что гарантирует, что обещания решаются последовательно по порядку.источник
Promise.each(filtes, fs.readFileAsync)
. Кстати, вам не нужно делать.bind(fs)
?new Array(int)
. Все, что нужно, - это предварительно установитьlength
пару ключ-значение, влияющую на то, сколько индексов используется во время итерации на основе длины. Она имеет ноль). влияние на фактическое индексирование массива или границы индекса)Это небольшое изменение другого ответа выше. Использование нативных обещаний:
объяснение
Если у вас есть эти задачи
[t1, t2, t3]
, то вышеупомянутое эквивалентноPromise.resolve().then(t1).then(t2).then(t3)
. Это поведение уменьшить.Как пользоваться
Для начала нужно составить список задач! Задача - это функция, которая не принимает аргументов. Если вам нужно передать аргументы вашей функции, то используйте
bind
или другие методы для создания задачи. Например:источник
Мое предпочтительное решение:
Это не принципиально отличается от других, опубликованных здесь, но:
Пример использования:
Проверено на разумных текущих Chrome (v59) и NodeJS (v8.1.2).
источник
Используйте
Array.prototype.reduce
и помните, чтобы обернуть свои обещания в функцию, иначе они уже будут запущены!красиво и легко ... вы должны иметь возможность повторно использовать одно и то же семя для производительности и т. д.
При использовании метода Reduce важно защититься от пустых массивов или массивов, состоящих только из одного элемента , поэтому лучше всего использовать эту технику:
и затем назовите это как:
источник
Я создал этот простой метод на объекте Promise:
Создайте и добавьте метод Promise.sequence в объект Promise.
Использование:
Лучшее в этом расширении объекта Promise заключается в том, что оно соответствует стилю обещаний. Promise.all и Promise.sequence вызываются одинаково, но имеют разную семантику.
предосторожность
Последовательное выполнение обещаний обычно не очень хороший способ использовать обещания. Обычно лучше использовать Promise.all и позволить браузеру запускать код как можно быстрее. Тем не менее, есть реальные варианты использования для этого - например, при написании мобильного приложения с использованием JavaScript.
источник
Promise.all
и своиPromise.sequence
. Один принимает итерацию обещаний, другой - массив функций, которые возвращают обещания.reduce
ответ на вопрос Бенджамина просто намного проще.Вы можете использовать эту функцию, которая получает список PromiseFactories:
Promise Factory - это простая функция, которая возвращает Promise:
Это работает, потому что фабрика обещаний не создает обещание, пока его не попросят. Он работает так же, как и функция then - фактически это то же самое!
Вы вообще не хотите оперировать множеством обещаний. Согласно спецификации Promise, как только обещание создано, оно начинает выполняться. Так что вы действительно хотите, это множество фабрик обещаний ...
Если вы хотите узнать больше об обещаниях, вы должны проверить эту ссылку: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
источник
Мой ответ основан на https://stackoverflow.com/a/31070150/7542429 .
Это решение возвращает результаты в виде массива, подобного Promise.all ().
Использование:
источник
Мне очень понравился ответ @ joelnet, но для меня этот стиль кодирования немного сложен, поэтому я потратил пару дней, пытаясь понять, как я могу выразить то же решение в более читабельной форме, и это мой взять, только с другим синтаксисом и некоторыми комментариями.
источник
Как заметил Берги, я думаю, что лучшее и понятное решение - использовать BlueBird.each, код ниже:
источник
Во-первых, вы должны понимать, что обещание выполняется во время создания.
Например, если у вас есть код:
Вам нужно изменить это на:
Тогда нам нужно последовательно соединить обещания:
выполняя
after()
, удостоверится, что обещание создано (и выполнено) только тогда, когда придет его время.источник
Я использую следующий код для расширения объекта Promise. Он обрабатывает отклонение обещаний и возвращает массив результатов
Код
пример
источник
Если вы хотите, вы можете использовать сокращение, чтобы сделать последовательное обещание, например:
это всегда будет работать последовательно.
источник
Используя современные ES:
источник
С Async / Await (если у вас есть поддержка ES7)
(вы должны использовать
for
цикл, а неforEach
потому, что у async / await есть проблемы с запуском в цикле forEach)Без Async / Await (используя Promise)
источник
forEach
(в соответствии с этим )Исходя из заголовка вопроса «Разрешать обещания одно за другим (т.е. по порядку)?», Мы можем понять, что ФП больше заинтересован в последовательной обработке обещаний по расчету, чем в последовательных вызовах как таковых .
Этот ответ предлагается:
Если одновременные вызовы действительно нежелательны, см. Ответ Бенджамина Грюнбаума, который полностью охватывает последовательные вызовы (и т. Д.).
Однако, если вы заинтересованы (для повышения производительности) в шаблонах, которые допускают одновременные вызовы с последующей последовательной обработкой ответов, тогда, пожалуйста, продолжайте читать.
Соблазнительно думать, что вы должны использовать
Promise.all(arr.map(fn)).then(fn)
(как я это делал много раз) или необычный сахар в Promise lib (особенно Bluebird), однако (с учетом этой статьи )arr.map(fn).reduce(fn)
шаблон будет работать с преимуществами, которые он дает :.then()
используются .Вот оно, написано для
Q
.Примечание: только этот один фрагмент
Q()
специфичен для Q. Для jQuery необходимо убедиться, что readFile () возвращает обещание jQuery. С A + libs, иностранные обещания будут ассимилированы.Ключевой момент здесь является сокращением в
sequence
обещании, что последовательности обработки изreadFile
обещаний , но не их создание.И как только вы это освоите, может быть, это немного ошеломляет, когда вы понимаете, что
.map()
сцена на самом деле не нужна! Вся работа, параллельные вызовы плюс последовательная обработка в правильном порядке, может быть выполнена вreduce()
одиночку, плюс дополнительное преимущество дополнительной гибкости для:Вот и
Q
снова.Это основной шаблон. Если вы хотите также передать данные (например, файлы или их преобразование) вызывающей стороне, вам понадобится мягкий вариант.
источник
sequence.then(() => filePromise)
антипаттерн - он не распространяет ошибки так быстро, как они могли (и создаетunhandledRejection
в библиотеках, которые их поддерживают). Вы скорее должны использоватьQ.all([sequence, filePromise])
или$.when(sequence, filePromise)
. Следует признать, что такое поведение может быть тем, что вам нужно, когда вы стремитесь игнорировать или пропускать ошибки, но вы должны по крайней мере упомянуть это как недостаток.unhandledRejection
события. В Bluebird вы можете обойти это, используяsequence.return(filePromise)
то же поведение, но отлично обрабатывая отклонения. Я не знаю ни одной ссылки, я только придумал это - я не думаю, что у «(анти) образца» еще есть имя.Ваш подход не плохой, но у него есть две проблемы: он глотает ошибки и использует антипаттерн Explicit Promise Construction.
Вы можете решить обе эти проблемы и сделать код чище, используя при этом одну и ту же общую стратегию:
источник
Если кому-то еще требуется гарантированный СТРОГО последовательный способ разрешения Promises при выполнении операций CRUD, вы также можете использовать следующий код в качестве основы.
До тех пор, пока вы добавите 'return' перед вызовом каждой функции, описывающей Promise, и будете использовать этот пример в качестве основы, следующий вызов функции .then () ПОСТОЯННО начнется после завершения предыдущей:
источник
Для последовательности обещаний можно использовать метод массива push и pop. Вы также можете выдвигать новые обещания, когда вам нужны дополнительные данные. Этот код я буду использовать в загрузчике React Infinite для загрузки последовательности страниц.
источник
Большинство ответов не включают в себя результаты ВСЕХ обещаний индивидуально, поэтому, если кто-то ищет это конкретное поведение, это возможное решение с использованием рекурсии.
Следует стиль
Promise.all
:Возвращает массив результатов в
.then()
обратном вызове.Если какое-то обещание не выполняется, оно немедленно возвращается в
.catch()
обратном вызове.Примечание об
tasks
объявлении массива :В этом случае не представляется возможным использовать следующие обозначения, как
Promise.all
было бы использовать:И мы должны использовать:
Причина в том, что JavaScript начинает выполнять обещание сразу же после его объявления. Если мы используем такие методы, как
Promise.all
, он просто проверяет, что состояние их всех равноfulfilled
илиrejected
, но не запускает само выполнение. С помощью() => promise()
мы останавливаем выполнение до его вызова.источник
Здесь ключ в том, как вы вызываете функцию сна. Вам нужно передать массив функций, который сам по себе возвращает обещание, а не массив обещаний.
источник
Это расширение того, как обрабатывать последовательность обещаний более общим способом, поддерживая динамические / бесконечные последовательности на основе реализации spex.sequence :
Мало того, что это решение будет работать с последовательностями любого размера, но вы можете легко добавить регулирование данных и распределение нагрузки к нему.
источник