Avec une promesse, pourquoi les navigateurs renvoient-ils deux fois un rejet mais pas une résolution deux fois?

10

J'ai du mal à comprendre javaScript promises. J'ai écrit le code suivant:

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

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

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

Je vois immédiatement cela dans ma console développeur Chrome: entrez la description de l'image ici

Mais après avoir attendu 5 secondes, le message passe automatiquement au noir comme cette image: entrez la description de l'image ici

Je n'ai jamais vu ce comportement auparavant entre mon code javaScript et une console de développeur, où mon code javaScript peut "modifier le contenu existant" dans la console de développeur.

J'ai donc décidé de voir si la même situation se produit resolveen écrivant ce code:

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

    resolve("hello world");
});

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

Mais dans cette situation, ma console de développeur n'affiche rien avant 5 secondes plus tard, sur laquelle elle s'imprime ensuite hello world.

Pourquoi les resolveet sont-ils rejecttraités si différemment en termes de moment où ils sont invoqués?


SUPPLÉMENTAIRE

J'ai également écrit ce code:

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);

Cela provoque plusieurs sorties vers la console du développeur. Erreur rouge au temps 0, le rouge devient noir au temps 5 secondes avec le texte errors hello world, puis un nouveau message d'erreur au temps 6 secondes errors 2 hello world, puis un message d'erreur rouge au temps 7 secondes. Maintenant, je suis très confus sur le nombre de fois où un est rejecteffectivement invoqué ... Je suis perdu ...

John
la source
1
Juste un aparté: var p = new Promise(function(resolve,reject){ reject(Error("hello world")); });peut être écrit de manière plus idiomatique et concise var p = Promise.reject(Error("hello world"));:-)
TJ Crowder
1
Super question.
TJ Crowder

Réponses:

11

Wow, c'est vraiment cool. Je n'avais jamais vu la console faire ça auparavant. (Il a cependant d' autres formes de comportement dynamique, alors ...) Voici ce qui se passe:

Dans le premier cas, l'exécution du code de tout ce qui se trouve en dehors setTimeoutdu code de votre rappel se termine et la pile d'exécution revient de sorte que seul le « code de plate-forme » (comme l'appelle la promesse / A +) s'exécute, pas le code JavaScript de l'espace utilisateur (pour le moment). À ce stade, la promesse est rejetée et rien n'a géré le rejet, il s'agit donc d'un rejet non géré et devtools vous le signale en tant que tel.

Ensuite , cinq secondes plus tard, votre rappel s'exécute et attache un gestionnaire de rejet. À ce stade, le rejet n'est plus géré. Apparemment, Chrome / V8 / devtools fonctionnent ensemble pour supprimer l'avertissement de rejet non géré de la console. Ce que vous voyez apparaître à la place est ce que vous sortez dans votre gestionnaire de rejet via console.log. Si vous attachez le gestionnaire de rejet plus tôt, vous n'obtiendrez pas cette erreur de rejet non gérée.

Cela ne se produit pas avec l'accomplissement car ne pas gérer l'accomplissement n'est pas une condition d'erreur. Ne pas gérer le rejet est.

TJ Crowder
la source
1
Oh, ça a du sens. J'ai remarqué que FireFox le gère légèrement différemment. Mais ok, ça a plus de sens maintenant.
John
1
J'ai écrit la même chose en réponse, mais j'ai donc chargé le vôtre, donc je n'ai pas posté le mien. Belle explication! +1
FZ