Considere o código a seguir que lê uma matriz de arquivos de maneira serial / seqüencial. readFiles
retorna uma promessa, que é resolvida apenas quando todos os arquivos foram lidos em sequência.
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!
});
};
O código acima funciona, mas não gosto de fazer recursões para que as coisas ocorram sequencialmente. Existe uma maneira mais simples de reescrever esse código para que eu não precise usar meu códigoreadSequential
função ?
Originalmente, tentei usar Promise.all
, mas isso fez com que todas as readFile
chamadas acontecessem simultaneamente, o que não é o que eu quero:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
javascript
promise
q
sequential
serial-processing
XåpplI'-I0llwlg'I -
fonte
fonte
readFileSequential()
já retornou antes do próximo ser chamado (por ser assíncrono, é concluído muito depois que a chamada de função original já retornou).Respostas:
Atualização 2017 : Eu usaria uma função assíncrona se o ambiente a suportar:
Se desejar, você pode adiar a leitura dos arquivos até precisar deles usando um gerador assíncrono (se o seu ambiente suportar):
Atualização: Pensando bem - eu poderia usar um loop for:
Ou de forma mais compacta, com redução:
Em outras bibliotecas promissoras (como quando e Bluebird), você tem métodos utilitários para isso.
Por exemplo, o Bluebird seria:
Embora não haja realmente nenhuma razão para não usar async, aguarde hoje.
fonte
Promise.resolve(Promise.resolve(15))
é idêntico aPromise.resolve(15)
.Aqui está como eu prefiro executar tarefas em série.
fonte
result = result.then(task);
Essa pergunta é antiga, mas vivemos em um mundo de ES6 e JavaScript funcional, então vamos ver como podemos melhorar.
Como as promessas são executadas imediatamente, não podemos apenas criar uma variedade de promessas, todas elas são disparadas em paralelo.
Em vez disso, precisamos criar uma matriz de funções que retorne uma promessa. Cada função será executada seqüencialmente, o que inicia a promessa por dentro.
Podemos resolver isso de algumas maneiras, mas minha maneira favorita é usar
reduce
.Fica um pouco complicado usando
reduce
em combinação com promessas, então eu quebrei o liner em algumas pequenas mordidas digestíveis abaixo.A essência desta função é usar
reduce
começando com um valor inicial dePromise.resolve([])
, ou uma promessa contendo uma matriz vazia.Essa promessa será então passada para o
reduce
método comopromise
. Essa é a chave para encadear cada promessa em seqüência. A próxima promessa a executar éfunc
e, quando osthen
incêndios, os resultados são concatenados e essa promessa é retornada, executando oreduce
ciclo com a próxima função de promessa.Depois que todas as promessas forem executadas, a promessa retornada conterá uma matriz de todos os resultados de cada promessa.
Exemplo ES6 (um revestimento)
Exemplo ES6 (detalhado)
Uso:
fonte
Array.prototype.concat.bind(result)
é a parte que estava faltando, tinha que fazer empurrando a resultados manualmente quais trabalhou, mas foi menos legalconsole.log.bind(console)
declaração em seu último exemplo agora é geralmente desnecessária. Hoje em dia você pode simplesmente passarconsole.log
. Por exemplo.serial(funcs).then(console.log)
. Testado nos nodejs atuais e no 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)))
e assim por diantePara fazer isso simplesmente no ES6:
fonte
files.forEach
se os arquivos são uma matriz.for (file of files) {...}
.Promise.resolve()
para criar uma promessa já resolvida na vida real. Por que não?Promise.resolve()
parece mais limpo quenew Promise(success => success())
.Promise.resolve();
no seu código.return sequence;
Coloqueisequence.then(() => { do stuff });
Utilitário simples para o Node.js. padrão promete:
ATUALIZAR
items-promessa é um pacote NPM pronto para usar, fazendo o mesmo.
fonte
Eu tive que executar muitas tarefas seqüenciais e usei essas respostas para criar uma função que cuidaria de lidar com qualquer tarefa seqüencial ...
A função recebe 2 argumentos + 1 opcional. O primeiro argumento é a matriz na qual estaremos trabalhando. O segundo argumento é a tarefa em si, uma função que retorna uma promessa; a próxima tarefa será iniciada apenas quando essa promessa for resolvida. O terceiro argumento é um retorno de chamada a ser executado quando todas as tarefas estiverem concluídas. Se nenhum retorno de chamada for passado, a função retornará a promessa criada para que possamos lidar com o fim.
Aqui está um exemplo de uso:
Espero que poupa alguém algum tempo ...
fonte
A melhor solução que consegui descobrir foi com
bluebird
promessas. Você pode fazer apenas asPromise.resolve(files).each(fs.readFileAsync);
garantias de que as promessas são resolvidas sequencialmente em ordem.fonte
Promise.each(filtes, fs.readFileAsync)
. Btw, você não tem que fazer.bind(fs)
?new Array(int)
. Tudo o que faz é predefinir olength
par de valores-chave, afetando quantos índices são usados durante a iteração baseada em comprimento. efeito sobre a indexação ou limites do índice)Essa é uma pequena variação de outra resposta acima. Usando promessas nativas:
Explicação
Se você tiver essas tarefas
[t1, t2, t3]
, o acima é equivalente aPromise.resolve().then(t1).then(t2).then(t3)
. É o comportamento de reduzir.Como usar
Primeiro você precisa construir uma lista de tarefas! Uma tarefa é uma função que não aceita argumentos. Se você precisar passar argumentos para sua função, use
bind
ou outros métodos para criar uma tarefa. Por exemplo:fonte
Minha solução preferida:
Não é fundamentalmente diferente dos outros publicados aqui, mas:
Exemplo de uso:
Testado no Chrome atual razoável (v59) e no NodeJS (v8.1.2).
fonte
Use
Array.prototype.reduce
e lembre-se de agrupar suas promessas em uma função, caso contrário elas já estarão em execução!agradável e fácil ... você poderá reutilizar a mesma semente para desempenho, etc.
É importante proteger-se contra matrizes vazias ou matrizes com apenas 1 elemento ao usar reduzir , portanto, esta técnica é sua melhor aposta:
e depois chame assim:
fonte
Eu criei este método simples no objeto Promise:
Crie e adicione um método Promise.sequence ao objeto Promise
Uso:
A melhor coisa sobre essa extensão do objeto Promise é que ela é consistente com o estilo das promessas. Promise.all e Promise.sequence são invocados da mesma maneira, mas possuem semânticas diferentes.
Cuidado
A execução seqüencial de promessas geralmente não é uma maneira muito boa de usar promessas. Geralmente é melhor usar Promise.all e deixar o navegador executar o código o mais rápido possível. No entanto, existem casos de uso reais para isso - por exemplo, ao escrever um aplicativo móvel usando javascript.
fonte
Promise.all
e o seuPromise.sequence
. Um aceita uma iterável de promessas, o outro assume uma série de funções que retornam promessas.reduce
como na resposta de Benjamin, é muito mais simples.Você pode usar esta função que é promissora
Promise Factory é apenas uma função simples que retorna uma Promise:
Funciona porque uma fábrica de promessas não cria a promessa até que seja solicitada. Funciona da mesma maneira que uma função then - na verdade, é a mesma coisa!
Você não quer operar com uma série de promessas. De acordo com a especificação Promise, assim que uma promessa é criada, ela começa a ser executada. Então, o que você realmente quer é uma variedade de fábricas promissoras ...
Se você quiser saber mais sobre as promessas, verifique este link: https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
fonte
Minha resposta com base em https://stackoverflow.com/a/31070150/7542429 .
Esta solução retorna os resultados como uma matriz como Promise.all ().
Uso:
fonte
Gostei muito da resposta da @ joelnet, mas para mim esse estilo de codificação é um pouco difícil de digerir, por isso passei alguns dias tentando descobrir como expressaria a mesma solução de uma maneira mais legível e essa é pegue, apenas com uma sintaxe diferente e alguns comentários.
fonte
Como Bergi notou, acho que a melhor e mais clara solução é usar o BlueBird.each, código abaixo:
fonte
Primeiro, você precisa entender que uma promessa é executada no momento da criação.
Então, por exemplo, se você tiver um código:
Você precisa alterá-lo para:
Em seguida, precisamos encadear promessas sequencialmente:
execução
after()
, garantirá que a promessa seja criada (e executada) somente quando chegar a hora.fonte
Eu uso o código a seguir para estender o objeto Promise. Ele lida com a rejeição das promessas e retorna uma variedade de resultados
Código
Exemplo
fonte
Se você quiser, pode usar o comando reduzir para fazer uma promessa sequencial, por exemplo:
sempre funciona em seqüência.
fonte
Usando o ES moderno:
fonte
Com Async / Await (se você tiver o suporte do ES7)
(você deve usar o
for
loop, e nãoforEach
porque o async / waitit tenha problemas em execução no loop forEach)Sem Async / Await (usando Promise)
fonte
forEach
(de acordo com isso )Com base no título da pergunta "Resolver promessas uma após a outra (ou seja, em sequência)?", Podemos entender que o OP está mais interessado no tratamento sequencial de promessas na liquidação do que em chamadas sequenciais per se .
Esta resposta é oferecida:
Se chamadas simultâneas não são realmente desejadas, consulte a resposta de Benjamin Gruenbaum, que abrange chamadas sequenciais (etc) de forma abrangente.
Se, no entanto, você estiver interessado (para melhorar o desempenho) em padrões que permitam chamadas simultâneas seguidas de manipulação sequencial de respostas, continue lendo.
É tentador pensar que você deve usar
Promise.all(arr.map(fn)).then(fn)
(como já fiz muitas vezes) ou o açúcar sofisticado de uma lib da Promise (principalmente o Bluebird), no entanto (com crédito para este artigo ) umarr.map(fn).reduce(fn)
padrão fará o trabalho, com as vantagens de:.then()
é usado apenas .Aqui está, escrito para
Q
.Nota: apenas esse fragmento,,
Q()
é específico para Q. Para o jQuery, você precisa garantir que readFile () retorne uma promessa do jQuery. Com A + libs, promessas estrangeiras serão assimiladas.A chave aqui é a
sequence
promessa da redução , que sequencia o tratamento dasreadFile
promessas, mas não a sua criação.E depois que você absorve isso, talvez seja um pouco surpreendente quando você percebe que o
.map()
palco não é realmente necessário! Todo o trabalho, chamadas paralelas e manipulação em série na ordem correta, pode ser realizadoreduce()
sozinho, além da vantagem adicional de maior flexibilidade para:Aqui está,
Q
novamente.Esse é o padrão básico. Se você também quisesse entregar dados (por exemplo, os arquivos ou algumas transformações deles) ao chamador, seria necessária uma variante moderada.
fonte
sequence.then(() => filePromise)
é um antipadrão - ele não propaga erros assim que eles podem (e criaunhandledRejection
em bibliotecas que os suportam). Você prefere usarQ.all([sequence, filePromise])
ou$.when(sequence, filePromise)
. É certo que esse comportamento pode ser o que você deseja quando pretende ignorar ou ignorar erros, mas você deve pelo menos mencionar isso como uma desvantagem.unhandledRejection
eventos. No Bluebird, você pode contornar isso usando osequence.return(filePromise)
que tem o mesmo comportamento, mas lida bem com as rejeições. Não conheço nenhuma referência, apenas a inventei - ainda não acho que o "(anti) padrão" tenha um nome.Sua abordagem não é ruim, mas possui dois problemas: engole erros e emprega o Antipadrão de construção de promessa explícita.
Você pode resolver esses dois problemas e tornar o código mais limpo, enquanto ainda emprega a mesma estratégia geral:
fonte
Se alguém precisar de uma maneira garantida de maneira estritamente sequencial de resolver o Promises ao executar operações CRUD, você também poderá usar o seguinte código como base.
Desde que você adicione 'return' antes de chamar cada função, descrevendo uma Promessa e use este exemplo como base, a próxima chamada de função .then () começará CONSISTENTEMENTE após a conclusão da anterior:
fonte
O método push e pop da matriz pode ser usado para a sequência de promessas. Você também pode enviar novas promessas quando precisar de dados adicionais. Este é o código que usarei no carregador React Infinite para carregar a sequência de páginas.
fonte
A maioria das respostas não inclui os resultados de TODAS as promessas individualmente, portanto, caso alguém esteja procurando esse comportamento específico, essa é uma solução possível usando a recursão.
Segue o estilo de
Promise.all
:Retorna a matriz de resultados no
.then()
retorno de chamada.Se alguma promessa falhar, ela retornará imediatamente no
.catch()
retorno de chamada.Nota sobre a
tasks
declaração do array :Neste caso, não é possível usar a seguinte notação como
Promise.all
usaria:E nós temos que usar:
O motivo é que o JavaScript começa a executar a promessa imediatamente após sua declaração. Se usarmos métodos como
Promise.all
esse, apenas verifica se o estado de todos eles éfulfilled
ourejected
, mas não inicia a própria exceção. Usando() => promise()
, paramos a execução até que seja chamada.fonte
Aqui, a chave é como você chama a função de suspensão. Você precisa passar uma matriz de funções que retorna uma promessa em vez de uma matriz de promessas.
fonte
Isso é para estender como processar uma sequência de promessas de maneira mais genérica, suportando sequências dinâmicas / infinitas, com base na implementação do spex.sequence :
Não apenas essa solução funcionará com sequências de qualquer tamanho, mas você pode adicionar facilmente otimização de dados e balanceamento de carga .
fonte