Qual é a diferença entre retornar valor ou Promise.resolve a partir de então ()


314

Qual é a diferença entre:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return "bbb";
  })
  .then(function(result) {
    console.log(result);
  });

e isto:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return Promise.resolve("bbb");
  })
  .then(function(result) {
    console.log(result);
  });

Estou perguntando como estou obtendo um comportamento diferente usando o serviço Angular e $ http com encadeamento .then (). Um pouco de código, portanto, primeiro o exemplo acima.


1
Que "comportamento diferente" você está vendo? Ambos os exemplos devem funcionar e se comportar aproximadamente da mesma maneira. O Promise.resolve()no segundo exemplo é desnecessário.
— JLRishe

4
@pixelbits Não há nada de errado em retornar uma promessa de um thenmanipulador; na verdade, é um aspecto essencial das especificações de promessas que você pode fazer.

Observe que isso funciona com thens arbitrariamente aninhados - o termo 'outras línguas' para isso é que thené a mape a flatMap.
— Benjamin Gruenbaum

1
na linha 2, por que você precisa chamar res ("aaa"), por que não pode retornar "aaa" ser suficiente e o Promise capturar para resolvê-lo () da mesma maneira que captura exceções para rejeitar ()?
— Sam Liddicott 9/11

1
@ SamLiddicott está com a mesma pergunta, enquanto as minas são um pouco mais complicadas: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result));esse código será interrompido (não será resolvido para sempre). Mas se eu mudar return "haha";para return res("haha");ele funcionará e alertará "haha". A busca (). Then () já envolveu "haha" em uma promessa resolvida?
— Shaung Cheng 11/03

Respostas:


138

A regra é que, se a função que está no thenmanipulador retornar um valor, a promessa será resolvida / rejeitada com esse valor e, se a função retornar uma promessa, o que acontece é que a próxima thencláusula será a thencláusula da promessa que a função retornou , portanto, neste caso, o primeiro exemplo cai na sequência normal dos thensvalores e imprime como Promise.resolve("bbb")seria de esperar, no segundo exemplo, o objeto de promessa que é retornado quando você o faz é o thenque é invocado ao encadear (para todos os efeitos). A maneira como ele realmente funciona é descrita abaixo com mais detalhes.

Citando as especificações Promises / A +:

O procedimento de resolução de promessa é uma operação abstrata, tendo como entrada uma promessa e um valor, que designamos como [[Resolve]](promise, x). Se xé um termo utilizável, ele tenta fazer com que a promessa adote o estado dex , sob a suposição de que x se comporta pelo menos um pouco como uma promessa . Caso contrário, cumpre a promessa com o valor x.

Esse tratamento de tabelas executáveis ​​permite que as implementações de promessa interoperem, desde que exponham um método compatível com Promises / A +. Ele também permite que as implementações do Promises / A + “assimilem” as implementações não conformes com métodos razoáveis.

O principal a notar aqui é esta linha:

se xfor uma promessa, adote seu estado [3.4]

link: https://promisesaplus.com/#point-49


4
"Adote seu estado" é uma maneira concisa e útil de expressar o comportamento quando um thenmanipulador retorna uma promessa. +1 para a referência de especificação.

69
Na verdade - a parte relevante das especificações aqui é o fato de [[Resolve]]ser chamado de capaz thene de valores, de modo que, essencialmente, envolve um valor com a promessa, return "aaa"é o mesmo que return Promise.resolve("aaa")e return Promise.resolve("aaa")é o mesmo que return Promise.resolve(Promise.resolve("aaa"))- já que resolver é idempotente chamando-o de um valor mais do que uma vez tem o mesmo resultado.
— Benjamin Gruenbaum

8
@Benjamin Gruenbaum significa que retornam "aaa"e return Promise.resolve("aaa")são permutáveis thenem todos os casos?
— CSnerd

9
Sim, é exatamente isso que significa.
— Benjamin Gruenbaum

118

Em termos simples, dentro de uma thenfunção de manipulador:

A) Quando xé um valor (número, string, etc):

  1. return x é equivalente a return Promise.resolve(x)
  2. throw x é equivalente a return Promise.reject(x)

B) Quando xé uma Promessa que já está liquidada (não está mais pendente):

  1. return xé equivalente a return Promise.resolve(x), se a Promessa já tiver sido resolvida.
  2. return xé equivalente a return Promise.reject(x), se a promessa já foi rejeitada.

C) Quando xhá uma promessa pendente:

  1. return xretornará uma promessa pendente e será avaliada no subsequente then.

Leia mais sobre este tópico nos documentos Promise.prototype.then () .


93

Ambos os seus exemplos devem se comportar da mesma forma.

Um valor retornado dentro de um then()manipulador se torna o valor de resolução da promessa retornada a partir dele then(). Se o valor retornado dentro da .then promessa for uma promessa, a promessa retornada por then()"adotará o estado" dessa promessa e resolverá / rejeitará exatamente como a promessa retornada.

No seu primeiro exemplo, você retorna "bbb"no primeiro then()manipulador, "bbb"sendo passado para o próximo then()manipulador.

No seu segundo exemplo, você retorna uma promessa que é imediatamente resolvida com o valor "bbb", "bbb"sendo passada para o próximo then()manipulador. (O Promise.resolve()aqui é estranho).

O resultado é o mesmo.

Se você pode nos mostrar um exemplo que realmente exibe comportamento diferente, podemos dizer por que isso está acontecendo.


1
Boa resposta! E quanto a Promise.resolve();vs return;?
— FabianTe

2
@FabianTe Esses também teriam o mesmo efeito, exceto com em undefinedvez de "bbb".
— JLRishe

51

Você já teve uma boa resposta formal. Eu pensei que deveria adicionar um pequeno.

As seguintes coisas são idênticas às promessas / promessas A + :

  • Chamando Promise.resolve(no seu caso Angular, isso é $q.when)
  • Chamando o construtor da promessa e resolvendo em seu resolvedor. No seu caso, é isso new $q.
  • Retornando um valor de um thenretorno de chamada.
  • Chamando Promise.all em uma matriz com um valor e extraia esse valor.

Portanto, todos os itens a seguir são idênticos para uma promessa ou valor simples X:

Promise.resolve(x);
new Promise(function(resolve, reject){ resolve(x); });
Promise.resolve().then(function(){ return x; });
Promise.all([x]).then(function(arr){ return arr[0]; });

E não é nenhuma surpresa, a especificação de promessas é baseada no Procedimento de resolução de promessas, que permite fácil interoperação entre bibliotecas (como $ q e promessas nativas) e facilita sua vida em geral. Sempre que uma resolução de promessa pode ocorrer, ocorre uma resolução criando consistência geral.


posso perguntar qual é o sentido de fazer Promise.resolve().then(function(){ return x; });? Eu encontrei um snippet fazendo algo semelhante (chamado de função dentro do thenbloco). Eu pensei que era mais ou menos como fazer um tempo limite, mas é um pouco mais rápido. jsben.ch/HIfDo
— Sampgun

Não faz sentido que seja o mesmo que Promise.resolve (x) em 99,99% dos casos. (o 0,001% é que estamos em um withbloco sobre um objeto ou proxy com um xacessador de propriedade que gera uma exceção. Nesse caso, Promise.resolve (x) causaria um erro gerado, mas Promise.resolve().then(function(){ return x; });seria uma promessa rejeitada, pois o erro é gerado em a then).
— Benjamin Gruenbaum

você vinculou uma blitz vazia ou não salvou. Enfim, eu não estava falando sobre as diferenças entre as declarações. Eu estava falando exatamente sobre o que escrevi. Só para ser mais claro, este é o trecho que eu estava falando: if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }. Aqui a promessa não é atribuída, então qual é o objetivo? Um tempo limite teria (mais ou menos) o mesmo efeito, ou não?
— Sampgun 14/02

1
Ele executa a chamada de forma assíncrona após todo o código síncrono, mas antes de qualquer E / S. Isso é chamado de "semântica de microtick".
— Benjamin Gruenbaum 14/02

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.