Maison  >  Questions et réponses  >  le corps du texte

Les refus ne se propagent pas en promesses enchaînées

Je n'arrive pas à comprendre pourquoi le rejet ne passe pas par la chaîne de promesses, j'espère que quelqu'un pourra m'aider à comprendre pourquoi. Pour moi, attacher une fonction à une séquence de promesses signifie que je compte sur l'intention de la promesse originale pour qu'elle soit remplie. C'est difficile à expliquer, alors permettez-moi d'abord de montrer un exemple de code de mon problème. (Remarque : cet exemple utilise Node et le module de nœud retardé. J'ai testé cela avec Dojo 1.8.3 et j'ai obtenu les mêmes résultats)

var d = require("deferred");

var d1 = d();

var promise1 = d1.promise.then(
    function(wins) { console.log('promise1 resolved'); return wins;},
    function(err) { console.log('promise1 rejected'); return err;});
var promise2 = promise1.then(
    function(wins) { console.log('promise2 resolved'); return wins;},
    function(err) { console.log('promise2 rejected'); return err;});
var promise3 = promise2.then(
    function(wins) { console.log('promise3 resolved'); return wins;},
    function(err) { console.log('promise3 rejected'); return err;});
d1.reject(new Error());

Le résultat de cette exécution est le résultat suivant :

promise1 rejected
promise2 resolved
promise3 resolved

Eh bien, pour moi, ce résultat n'a pas de sens. En s'ajoutant à cette chaîne de promesses, chacun implique alors l'intention que cela dépendra de la résolution réussie de d1 et du résultat transmis le long de la chaîne. Si la promesse dans promise1 ne reçoit pas de valeur wins, mais une valeur err dans son gestionnaire d'erreurs, comment la prochaine promesse de la chaîne peut-elle appeler sa fonction success ? Il ne peut pas transmettre une valeur significative à la promesse suivante car il n'obtient pas la valeur elle-même.

Je peux décrire mon idée d'une autre manière : il y a trois personnes : John, Ginger et Bob. John possède un magasin de widgets. Ginger est entré dans son magasin et a demandé un sac de gadgets de différentes couleurs. Il ne les avait pas en stock, il a donc envoyé une demande à son revendeur pour qu'il lui soit expédié. Pendant ce temps, il donne à Ginger un chèque de pluie, disant qu'il lui doit le sac de widgets. Bob aperçoit Ginger en train de récupérer les widgets et lui demande de récupérer le widget bleu lorsqu'elle en a fini avec eux. Elle a accepté et lui a remis une note disant qu'elle serait d'accord. Maintenant, le revendeur de John ne trouve aucun widget dans son approvisionnement et le fabricant ne fabrique plus les widgets, alors ils en informent John, qui à son tour informe Ginger qu'elle ne peut pas obtenir les widgets. Comment Bob peut-il obtenir le widget bleu de Ginger alors qu'il ne reçoit rien lui-même ?

Ma troisième perspective, plus réaliste, sur cette question est la suivante. Disons que j'ai deux valeurs que je souhaite mettre à jour dans la base de données. L'un dépend de l'identifiant de l'autre, mais je ne peux pas obtenir l'identifiant tant que je ne l'ai pas inséré dans la base de données et obtenu le résultat. De plus, la première insertion dépend de la requête de la base de données. La promesse renvoyée par l'appel à la base de données est ce que j'utilise pour enchaîner les deux appels dans une séquence.

var promise = db.query({parent_id: value});
promise.then(function(query_result) {
    var first_value = {
        parent_id: query_result[0].parent_id
    }
    var promise = db.put(first_value);
    promise.then(function(first_value_result) {
        var second_value = {
            reference_to_first_value_id: first_value_result.id
        }
        var promise = db.put(second_value);
        promise.then(function(second_value_result) {
            values_successfully_entered();
        }, function(err) { return err });
    }, function(err) { return err });
}, function(err) { return err });

Maintenant, dans ce cas, si db.query échoue, il appellera la première fonction err de then . Mais ensuite, il appelle la fonction de réussite de la prochaine promesse. Bien que cette promesse attend le résultat de la première valeur, elle reçoit un message d'erreur de sa fonction de gestion des erreurs.

Donc, ma question est la suivante : si je dois tester les erreurs dans la fonction de réussite, pourquoi ai-je une fonction de gestion des erreurs ?

Désolé, cet article est trop long. Je ne sais tout simplement pas comment l'expliquer autrement.

Mises à jour et corrections

(Remarque : j'ai supprimé une réponse que j'avais faite à certains commentaires. Donc, si quelqu'un commentait ma réponse, son commentaire pourrait apparaître hors de son contexte maintenant que je l'ai supprimé. Désolé, j'essaie d'être aussi court que possible. possible)

Merci à tous pour vos réponses. Je tiens d'abord à m'excuser auprès de tout le monde pour la mauvaise rédaction de ma question, en particulier mon pseudocode. J'ai été un peu trop agressif en essayant d'être bref.

Merci pour la réponse Bergi, je pense avoir trouvé l'erreur dans ma logique. Je pense que j'oublie peut-être un autre problème qui est à l'origine des problèmes que je rencontre. Cela peut entraîner un fonctionnement différent de la chaîne de promesses que je pensais. Je suis encore en train de tester différents éléments du code, donc je ne peux même pas formuler une question appropriée pour voir ce que je fais de mal. Cependant, je voulais informer tout le monde de la situation et vous remercier pour votre aide.

P粉193307465P粉193307465382 Il y a quelques jours2853

répondre à tous(2)je répondrai

  • P粉155710425

    P粉1557104252023-10-24 16:07:16

    @Jordan Tout d'abord, comme l'ont souligné les commentateurs, votre premier exemple produira certainement les résultats que vous attendez lorsque vous utilisez une bibliothèque paresseuse :

    promise1 rejected
    promise2 rejected
    promise3 rejected

    Deuxièmement, même s'il produit le résultat que vous suggérez, cela n'affecte pas le flux d'exécution du deuxième extrait de code, qui est un peu différent et ressemble plus à :

    promise.then(function(first_value) {
        console.log('promise1 resolved');
        var promise = db.put(first_value);
        promise.then(function (second_value) {
             console.log('promise2 resolved');
             var promise = db.put(second_value);
             promise.then(
                 function (wins) { console.log('promise3 resolved'); },
                 function (err) { console.log('promise3 rejected'); return err; });
        }, function (err) { console.log('promise2 rejected'); return err;});
    }, function (err) { console.log('promise1 rejected'); return err});

    Et, si la première promesse est rejetée, seul résultat :

    promise1 rejected

    Cependant (passons à la partie amusante) même la bibliothèque différée revient définitivement 3 x returned,大多数其他承诺库将返回 1 x returned, 2 x 已解决 (ce qui laisse supposer que vous obtenez ces résultats en utilisant une autre bibliothèque Promise).

    Également déroutant, d'autres bibliothèques se comportent plus correctement. Laisse-moi expliquer.

    Dans le monde synchrone, la contrepartie du « rejet de promesse » est 抛出。因此从语义上讲,同步中的异步 deferred.reject(new Error()) 等于 throw new Error(). Dans votre exemple, vous ne lancez pas d'erreurs dans le rappel synchrone, vous les renvoyez simplement, vous passez donc au flux de réussite où les erreurs sont les valeurs de réussite. Pour vous assurer que le rejet va plus loin, vous devez renvoyer l'erreur :

    function (err) { console.log('promise1 rejected'); throw err; });

    Maintenant, la question est : pourquoi la bibliothèque de retard traite-t-elle l'erreur renvoyée comme un rejet ?

    La raison est que le rejet en cas de retard de travail est un peu différent. Dans la bibliothèque différée, la règle est la suivante : Lorsqu'une instance d'erreur se produit, la promesse est rejetée , donc même si vous exécutez le rappel deferred.resolve(new Error()) 它也会起作用如 deferred.reject(new Error()) ,如果你尝试执行 deferred.reject(notAnError) ,它会抛出一个异常,表示该 Promise 只能被拒绝有错误的实例。这清楚地表明了为什么从 then, l'erreur renvoyée rejette la promesse.

    Il existe un raisonnement valable derrière la logique

    defer, mais elle ne correspond toujours pas à la façon dont throw fonctionne en JavaScript, ce comportement devrait donc changer dans la version retardée v0.7.

    Court résumé :

    Pour éviter toute confusion et résultats inattendus, suivez simplement les règles de bonne pratique :

    1. Rejetez toujours les promesses avec des instances erronées (en suivant les règles du monde synchrone, où lancer des valeurs non erronées est considéré comme une mauvaise pratique).
    2. Lancez une erreur via le rappel de synchronisation rejection (le renvoi d'une erreur ne garantit pas le rejet).

    Adhérez à ce qui précède et vous obtiendrez des résultats cohérents et attendus dans les bibliothèques différées et d'autres bibliothèques Promise populaires.

    répondre
    0
  • P粉376738875

    P粉3767388752023-10-24 12:09:36

    Non. Ce que vous décrivez n'est pas une chaîne, mais simplement l'ajout de tous les rappels au d1。然而,如果您想使用 then 链接某些内容,promise2 的结果取决于 promise1 的分辨率以及 promise1 的分辨率以及 < code>然后rappel qui le gère.

    La documentation indique :

    .then 方法通常根据 Promises 来看待/A 规范(或者更严格的Promsises/A+ )。这意味着回调 shell 返回的 Promise 将被同化为 Promise2 的解析,如果没有成功/错误处理程序,相应的结果将直接传递给 Promise2 code> - vous pouvez donc simplement omettre le gestionnaire pour propager l'erreur.

    Cependant, si l'erreur est gérée, la promise2 résultante est considérée comme corrigée et sera remplie avec cette valeur. Si vous ne voulez pas que cela se produise, vous devez promise2 被视为已修复,并将用该值来实现。如果您不希望出现这种情况,则必须重新抛出re-lancer l'erreur comme dans une clause try-catch. Alternativement, vous pouvez renvoyer une promesse rejetée (en attente) du gestionnaire. Je ne sais pas quel est le rejet du Dojo, mais :

    var d1 = d();
    
    var promise1 = d1.promise.then(
        function(wins) { console.log('promise1 resolved'); return wins;},
        function(err) { console.log('promise1 rejected'); throw err;});
    var promise2 = promise1.then(
        function(wins) { console.log('promise2 resolved'); return wins;},
        function(err) { console.log('promise2 rejected'); throw err;});
    var promise3 = promise2.then(
        function(wins) { console.log('promise3 resolved'); return wins;},
        function(err) { console.log('promise3 rejected'); throw err;});
    d1.reject(new Error());

    Il ne devrait pas pouvoir le faire. Sans gestionnaire d'erreurs, il ne percevrait que le message indiquant qu'il n'y a plus de widgets ((de John) de Ginger. Cependant, si Ginger définit un gestionnaire d'erreurs pour cette situation, si John ou son revendeur n'a plus de widgets bleus, elle). peut encore tenir sa promesse d'offrir à Bob un widget vert depuis sa propre cabane

    .

    Pour convertir le rappel d'erreur en méta, renvoyez l'erreur du gestionnaire comme en disant "S'il ne reste aucun widget, donnez-lui simplement un commentaire indiquant qu'il ne reste aucun widget - c'est la même chose que le bon widget requis".

    ...ce qui signifie que l'erreur a été traitée ici. Si ce n'est pas le cas, omettez simplement le rappel d'erreur. BTW, vos rappels de réussite ne tiennent pas la promesse qu'ils créent, ils semblent donc inutiles. La bonne est : 返回

    var promise = db.query({parent_id: value});
    promise.then(function(query_result) {
        var first_value = {
            parent_id: query_result[0].parent_id
        }
        var promise = db.put(first_value);
        return promise.then(function(first_value_result) {
            var second_value = {
                reference_to_first_value_id: first_value_result.id
            }
            var promise = db.put(second_value);
            return promise.then(function(second_value_result) {
                return values_successfully_entered();
            });
        });
    });

    Ou, puisque vous n'avez pas besoin de fermeture pour accéder à la valeur du résultat du rappel précédent, même :

    db.query({parent_id: value}).then(function(query_result) {
        return db.put({
            parent_id: query_result[0].parent_id
        });
    }).then(function(first_value_result) {
        return db.put({
            reference_to_first_value_id: first_value_result.id
        });
    }.then(values_successfully_entered);

    répondre
    0
  • Annulerrépondre