Pourquoi le constructeur Promise a-t-il besoin d'un exécuteur testamentaire?

Lors de l'utilisation de Promises , pourquoi ne peut-il pas se déclencher pour resolve et reject définissez-vous ailleurs dans la base de code?

Je ne comprends pas pourquoi la resolve et le reject logique devraient être localisés là où la promesse est déclarée. Est-ce un oubli, ou est-ce qu'il y a un avantage à exiger le paramètre de l' executor ?


Je crois que la fonction de l'exécuteur testamentaire devrait être facultative et que son existence devrait déterminer si la promesse réside ou non. La promesse serait beaucoup plus extensible sans ces mandats, puisque vous ne devez pas commencer à vous lancer immédiatement. La promesse devrait également être réinitialisée. C'est un commutateur 1, 1 ou 0, resolve() ou reject() . Il existe une multitude de résultats parallèles et séquentiels qui peuvent être joints: promise.then(parallel1) et promise.then(parallel2) et promise.then(seq1).then(seq2) également. promise.then(seq1).then(seq2) mais les joueurs privilégiant les références ne peuvent pas résoudre / rejeter INTO l'interrupteur

Vous pouvez construire un arbre de résultats plus tard, mais vous ne pouvez pas les modifier, et vous ne pouvez pas modifier les racines (déclencheurs d'entrée)

Honnêtement, l'arbre des résultats séquentiels devrait être éditable aussi … dire que vous voulez épisser une étape et faire autre chose à la place, après avoir déclaré de nombreuses chaînes prometteuses. Il n'est pas logique de reconstituer la promesse et chaque fonction séquentielle, d'autant plus que vous ne pouvez même pas rejeter ou détruire la promesse non plus …

C'est ce qu'on appelle le modèle constructeur révélateur inventé par Domenic.

Fondamentalement, l'idée est de vous donner accès à des parties d'un objet alors que cet objet n'est pas encore entièrement construit. Citation de Domenic:

J'appelle cela le modèle constructeur révélateur car le constructeur Promise révèle ses capacités internes, mais seulement le code qui construit la promesse en question. La capacité de résoudre ou de rejeter la promesse n'est révélée que pour le code de construction, et il est crucial de ne pas révéler à quiconque utilise la promesse. Donc, si nous distribuons p à un autre consommateur, disons

Le passé

Au début, les promesses ont fonctionné avec des objets différés, cela est vrai dans les promesses Twisted promesses promises par JavaScript. Cela est encore vrai (mais souvent obsolète) dans les anciennes implémentations comme les $q , Q, jQuery d'Angular et les anciennes versions de bluebird.

L'API a été quelque chose comme:

 var d = Deferred(); d.resolve(); d.reject(); d.promise; // the actual promise 

Cela a fonctionné, mais cela a eu un problème. Les différés et le constructeur prometteur sont généralement utilisés pour convertir des API non prometteuses en promesses. Il existe un problème «célèbre» dans JavaScript appelé Zalgo – essentiellement, cela signifie qu'une API doit être synchrone ou asynchrone, mais jamais les deux à la fois.

La chose est – avec différé il est possible de faire quelque chose comme:

 function request(param) { var d = Deferred(); var options = JSON.parse(param); d.ajax(function(err, value) { if(err) d.reject(err); else d.resolve(value); }); } 

Il y a un bug subtil caché ici – si param n'est pas un JSON valide, cette fonction se déclenche de façon synchrone, ce qui signifie que je dois envelopper toutes les promesses de retour de la fonction dans a } catch (e) { et a .catch(e => pour attraper tout les erreurs.

Le constructeur prometteur saisit ces exceptions et les convertit en rejet, ce qui signifie que vous ne devrez jamais vous soucier d'exceptions synchrones vs asynchrones avec des promesses. (Il vous garde de l'autre côté en exécutant toujours les rappels "dans la prochaine coche").

En outre, il a également besoin d'un type supplémentaire chaque développeur doit apprendre à savoir où le constructeur prometteur n'est pas jolie.

FYI, si vous mourrez d'utiliser l'interface différée plutôt que l'interface de l'exécuteur Promise malgré toutes les bonnes raisons contre l'interface différée, vous pouvez coder une fois de façon banale une fois, puis l'utiliser partout (personnellement je pense que c'est une mauvaise idée de coder cela , Mais votre volume de questions sur ce sujet vous suggère de penser différemment, alors voici):

 function Deferred() { var self = this; var p = this.promise = new Promise(function(resolve, reject) { self.resolve = resolve; self.reject = reject; }); this.then = this.promise.then.bind(p); this.catch = this.promise.catch.bind(p); } 

Maintenant, vous pouvez utiliser l'interface que vous semblez demander:

 var d = new Deferred(); d.resolve(); d.reject(); d.promise; // the actual promise d.then(...) // can use .then() on either the Deferred or the Promise d.promise.then(...) 

Ou, vous pouvez faire ce que vous avez demandé dans votre question en utilisant ce constructeur Deferred()

 var request = new Deferred(); request.resolve(); request.then(handleSuccess, handleError); 

Mais, il a les inconvénients signalés par Benjamin et n'est pas considéré comme le meilleur moyen de coder des promesses.

Quelque chose de similaire montre ici sur MDN .