Com uma promessa, por que os navegadores retornam uma rejeição duas vezes, mas não uma resolução duas vezes?

10

Estou tendo problemas para entender o javaScript promises. Eu escrevi o seguinte código:

var p = new Promise(function(resolve,reject){

    reject(Error("hello world"));
});

setTimeout(()=>p.catch(e=>console.log(e)),5000);

Vejo isso imediatamente no meu console do desenvolvedor Chrome: insira a descrição da imagem aqui

Mas depois de esperar 5 segundos, a mensagem muda automaticamente para preto como esta imagem: insira a descrição da imagem aqui

Nunca vi esse comportamento antes entre meu código javaScript e um console do desenvolvedor, onde meu código javaScript pode "modificar o conteúdo existente" no console do desenvolvedor.

Então eu decidi ver se a mesma situação ocorre resolveescrevendo este código:

var p = new Promise(function(resolve,reject){

    resolve("hello world");
});

setTimeout(()=>p.then(e=>console.log(e)),5000);

Mas nessa situação, meu console do desenvolvedor não mostra nada até 5 segundos depois, para o qual é impresso hello world.

Por que o resolvee rejecttratadas de modo diferente em termos de quando eles são chamados?


EXTRA

Eu também escrevi este código:

var p = new Promise(function(resolve,reject){

    reject(Error("hello world"));
});

setTimeout(()=>p.catch(e=>console.log("errors",e)),5000);
setTimeout(()=>p.catch(e=>console.log("errors 2",e)),6000);
setTimeout(()=>p.catch(null),7000);

Isso causa várias saídas para o console do desenvolvedor. Erro vermelho no tempo 0, vermelho muda para preto no tempo 5 segundos com o texto errors hello world, uma nova mensagem de erro no tempo 6 segundos errors 2 hello worlde uma mensagem de erro vermelha no tempo 7 segundos. Agora estou muito confuso sobre quantas vezes uma pessoa rejecté realmente chamada ... Estou perdida ...

John
fonte
11
Apenas um aparte: var p = new Promise(function(resolve,reject){ reject(Error("hello world")); });pode ser mais idiomaticamente e concisa escrito como var p = Promise.reject(Error("hello world"));:-)
TJ Crowder
11
Pergunta incrível.
TJ Crowder

Respostas:

11

Uau, isso é muito legal. Eu nunca tinha visto o console fazer isso antes. ( Porém, ele tem outras formas de comportamento dinâmico, então ...) Aqui está o que está acontecendo:

No primeiro caso, a execução do código de tudo o que estiver fora setTimeoutdo código do retorno de chamada é concluída e a pilha de execução retorna para que apenas o " código da plataforma " (como a especificação Promises / A + o chame) esteja sendo executado, e não o código JavaScript da área do usuário (no momento). Nesse ponto, a promessa é rejeitada e nada lidou com a rejeição; portanto, é uma rejeição não tratada e o devtools a relata como tal.

Então , cinco segundos depois, seu retorno de chamada é executado e anexa um manipulador de rejeição. Nesse ponto, a rejeição não é mais tratada. Aparentemente, o Chrome / V8 / devtools trabalha em conjunto para remover o aviso de rejeição não tratada do console. Em vez disso, o que você vê é o que você produz no seu manipulador de rejeições via console.log. Se você anexasse o manipulador de rejeição mais cedo, não receberia esse erro de rejeição não tratado.

Isso não acontece com o cumprimento porque não lidar com o cumprimento não é uma condição de erro. Não lidar com rejeição é.

TJ Crowder
fonte
11
Oh, isso faz sentido. Notei que o FireFox lida com isso de maneira um pouco diferente. Mas tudo bem, faz mais sentido agora.
John
11
Eu escrevi exatamente o mesmo em resposta, mas SO carregou o seu, então eu não postei o meu. Boa explicação! +1
FZs 08/10/1919