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.

spirytus
fonte
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

Hrishi
fonte
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 () .

Arian Acosta
fonte
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.

JLRishe
fonte
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.

Benjamin Gruenbaum
fonte
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