Quando eu chamo essa promessa, a saída não corresponde à sequência de chamadas de função. O .then
vem antes do .catch
, embora a promessa com .then
tenha sido chamada depois. Qual é o motivo disso?
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response: ", response))
.catch((error) => console.log("error: ", error));
verifier(5, 4)
.then((response) => console.log("response: ", response))
.catch((error) => console.log("error: ", error));
resultado
node promises.js
response: true
error: false
javascript
node.js
Gustavo Alves
fonte
fonte
Respostas:
Essa é uma pergunta legal para se chegar ao fundo.
Quando você faz isso:
verifier(3,4).then(...)
que retorna uma nova promessa que requer outro ciclo de volta ao loop de eventos antes que a promessa rejeitada possa executar o
.catch()
manipulador a seguir. Esse ciclo extra dá a próxima sequência:verifier(5,4).then(...)
a chance de executar seu
.then()
manipulador antes da linha anterior.catch()
porque ele já estava na fila antes que o.catch()
manipulador da primeira entre na fila e os itens sejam executados da fila em ordem FIFO.Observe que, se você usar o
.then(f1, f2)
formulário no lugar do.then().catch()
, ele será executado quando você espera, porque não há promessa adicional e, portanto, nenhuma marca adicional envolvida:const verifier = (a, b) => new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false))); verifier(3, 4) .then((response) => console.log("response (3,4): ", response), (error) => console.log("error (3,4): ", error) ); verifier(5, 4) .then((response) => console.log("response (5,4): ", response)) .catch((error) => console.log("error (5,4): ", error));
Observe, também rotulei todas as mensagens para que você possa ver de qual
verifier()
chamada elas vêm, o que torna muito mais fácil ler a saída.ES6 Especificação sobre pedido de retorno de chamada de promessa e explicação mais detalhada
A especificação ES6 nos diz que os "trabalhos" de promessa (já que chama um retorno de chamada de um
.then()
ou.catch()
) são executados em ordem FIFO com base em quando são inseridos na fila de trabalhos. Ele não nomeia especificamente FIFO, mas especifica que novos trabalhos são inseridos no final da fila e os trabalhos são executados a partir do início da fila. Isso implementa o pedido FIFO.PerformPromiseThen (que executa o retorno de chamada de
.then()
) levará a EnqueueJob, que é como o manipulador de resolução ou rejeição é programado para ser executado de fato. EnqueueJob especifica que o trabalho pendente é adicionado no final da fila de trabalhos. Em seguida, a operação NextJob puxa o item da frente da fila. Isso garante ordem FIFO em trabalhos de serviço da fila de trabalhos Promise.Portanto, no exemplo da pergunta original, obtemos os retornos de chamada para a
verifier(3,4)
promessa e averifier(5,4)
promessa inserida na fila de tarefas na ordem em que foram executadas porque ambas as promessas originais foram cumpridas. Então, quando o intérprete volta ao loop de eventos, ele primeiro pega overifier(3,4)
trabalho. Essa promessa foi rejeitada e não há retorno de chamada para isso noverifier(3,4).then(...)
. Portanto, o que ele faz é rejeitar a promessa queverifier(3,4).then(...)
retornou e que faz com que overifier(3,4).then(...).catch(...)
manipulador seja inserido no jobQueue.Em seguida, ele volta ao loop de eventos e o próximo trabalho que extrai de jobQueue é o
verifier(5, 4)
trabalho. Isso tem uma promessa resolvida e um manipulador de resolução, portanto, ele chama esse manipulador. Isso faz com que aresponse (5,4):
saída seja exibida.Em seguida, ele volta para o loop de eventos e o próximo trabalho que puxa do jobQueue é o
verifier(3,4).then(...).catch(...)
trabalho onde é executado e isso faz com que aerror (3,4)
saída seja mostrada.É porque
.catch()
na 1ª cadeia há um nível de promessa mais profundo em sua cadeia do que.then()
na 2ª cadeia que causa a ordem que você relatou. E é porque as cadeias de promessa são percorridas de um nível para o próximo por meio da fila de tarefas na ordem FIFO, não de forma síncrona.Recomendação geral sobre confiar neste nível de detalhe de programação
Para sua informação, em geral, tento escrever um código que não dependa desse nível de conhecimento detalhado de temporização. Embora seja curioso e ocasionalmente útil de entender, é um código frágil, pois uma mudança simples e aparentemente inócua no código pode levar a uma mudança no tempo relativo. Portanto, se o tempo for crítico entre duas cadeias como essa, prefiro escrever o código de uma forma que force o tempo da maneira que desejo, em vez de confiar nesse nível de compreensão detalhada.
fonte
.then()
tem que retornar uma nova promessa que ela mesma tem que resolver / rejeitar de forma assíncrona em um tick futuro que é o que leva a essa ordem. Você conhece alguma implementação que não usa ordenação FIFO de callbacks concorrentes?await
, no entanto).PerformPromiseThen
levará aEnqueueJob
que é como o manipulador de resolução ou rejeição é programado para ser chamado. EnqueueJob especifica que o trabalho pendente é adicionado no final da fila de trabalhos. Em seguida, a operação NextJob puxa o item da frente da fila. Isso garante a ordem FIFO na fila de trabalhos do Promise.await
no ES11? Um link é suficiente. Obrigado!!Promise.resolve() .then(() => console.log('a1')) .then(() => console.log('a2')) .then(() => console.log('a3')) Promise.resolve() .then(() => console.log('b1')) .then(() => console.log('b2')) .then(() => console.log('b3'))
Em vez da saída a1, a2, a3, b1, b2, b3, você verá a1, b1, a2, b2, a3, b3 por causa do mesmo motivo - a cada um retorna uma promessa e vai para o final do loop de eventos fila. Então podemos ver essa "corrida de promessas". O mesmo é quando há algumas promessas aninhadas.
fonte