Les promesses peuvent-elles avoir plusieurs arguments pour onFulfilled?


127

Je suis la spécification ici et je ne sais pas si cela permet à onFulfilled d'être appelé avec plusieurs arguments. Par exemple:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled('arg1', 'arg2');
})

tel que mon code:

promise.then(function(arg1, arg2){
    // ....
});

recevrait à la fois arg1et arg2?

Je me fiche de la façon dont une mise en œuvre de promesses spécifiques le fait, je souhaite suivre de près les spécifications du w3c pour les promesses.


À titre indicatif, j'ai trouvé que l'utilisation de github.com/then/promise (qui est une implémentation barebones) montre qu'il ne fournit en fait pas le 2ème argument
badunk

2
Vous souhaitez utiliser Bluebird avec .spread. - aussi, arrêtez de vous soucier de la spécification, la spécification concerne l' interopérabilité entre les implémentations et est minimale par conception.
Benjamin Gruenbaum

Réponses:


130

Je suis la spécification ici et je ne sais pas si cela permet à onFulfilled d'être appelé avec plusieurs arguments.

Non, seul le premier paramètre sera traité comme valeur de résolution dans le constructeur de promesse. Vous pouvez résoudre avec une valeur composite comme un objet ou un tableau.

Je me fiche de la façon dont une mise en œuvre de promesses spécifiques le fait, je souhaite suivre de près les spécifications du w3c pour les promesses.

C'est là que je crois que vous vous trompez. La spécification est conçue pour être minimale et est conçue pour interopérer entre les bibliothèques de promesse. L'idée est d'avoir un sous-ensemble que les futurs DOM, par exemple, peuvent utiliser de manière fiable et que les bibliothèques peuvent consommer. Les implémentations Promise font ce que vous demandez .spreaddepuis un moment maintenant. Par exemple:

Promise.try(function(){
    return ["Hello","World","!"];
}).spread(function(a,b,c){
    console.log(a,b+c); // "Hello World!";
});

Avec Bluebird . Une solution si vous voulez cette fonctionnalité est de la polyfill.

if (!Promise.prototype.spread) {
    Promise.prototype.spread = function (fn) {
        return this.then(function (args) {
            return Promise.all(args); // wait for all
        }).then(function(args){
         //this is always undefined in A+ complaint, but just in case
            return fn.apply(this, args); 
        });
    };
}

Cela vous permet de faire:

Promise.resolve(null).then(function(){
    return ["Hello","World","!"]; 
}).spread(function(a,b,c){
    console.log(a,b+c);    
});

Avec les promesses natives à l'aise violon . Ou utilisez la diffusion qui est maintenant (2018) courante dans les navigateurs:

Promise.resolve(["Hello","World","!"]).then(([a,b,c]) => {
  console.log(a,b+c);    
});

Ou avec wait:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);

2
Notez que d'autres bibliothèques (comme Q) prennent également en charge .spreadcomme Bluebird - la raison pour laquelle ce n'est pas dans la spécification est que garder la spécification minimale est vraiment un gros problème afin de permettre l'interopérabilité entre le code et les bibliothèques.
Benjamin Gruenbaum

Deuxième remarque - vous voudrez peut-être appeler Promise.allle tableau avant d'appliquer la fonction plutôt que de simplement l' .thenutiliser pour gérer certaines bibliothèques de sucre. Ce n'est pas obligatoire, mais c'est mignon.
Benjamin Gruenbaum

1
Promies.all est obligatoire avec votre implémentation, bien que vous puissiez simplement changer l'implémentation enreturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija

14
spreadest un palliatif. ES6 introduit la déstructuration et l'opérateur de repos / propagation, qui éliminent le besoin spreadpur et simple. .then(([a, b, c]) => {})
Kris Kowal

3
@KrisKowal Notez que .spread () fait implicitement .Toutes () mais la syntaxe déstructurant ES6 ne fonctionne pas -> bluebirdjs.com/docs/api/spread.html
Gomino

66

Vous pouvez utiliser la déstructuration E6:

Déstructuration d'objets:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled({arg1: value1, arg2: value2});
})

promise.then(({arg1, arg2}) => {
    // ....
});

Déstructuration du tableau:

promise = new Promise(function(onFulfilled, onRejected){
    onFulfilled([value1, value2]);
})

promise.then(([arg1, arg2]) => {
    // ....
});

3
Un exemple serait bien et utile avec cette réponse!
Rahul Verma

19

La valeur de réalisation d'une promesse est parallèle à la valeur de retour d'une fonction et la raison de rejet d'une promesse est parallèle à l'exception lancée d'une fonction. Les fonctions ne peuvent pas renvoyer plusieurs valeurs, les promesses ne doivent donc pas avoir plus d'une valeur d'exécution.


4

Pour autant que je sache, en lisant la spécification ES6 Promise et la spécification standard de la promesse, il n'y a pas de clause empêchant une implémentation de gérer ce cas - cependant, elle n'est pas implémentée dans les bibliothèques suivantes:

Je suppose que la raison pour laquelle ils omettent les résolutions multi-arg est de rendre l'ordre de changement plus succinct (c'est-à-dire que vous ne pouvez renvoyer qu'une seule valeur dans une fonction, cela rendrait le flux de contrôle moins intuitif) Exemple:

new Promise(function(resolve, reject) {
   return resolve(5, 4);
})
.then(function(x,y) {
   console.log(y);
   return x; //we can only return 1 value here so the next then will only have 1 argument
})
.then(function(x,y) {
    console.log(y);
});

8
Q ne prend pas en charge les résolutions à valeurs multiples car les promesses servent de proxy pour le résultat d'un appel de fonction mais peuvent également proxy pour des objets distants. Dans ces deux cas, un tableau est la seule représentation sensible d'une valeur composée. Avec l'ajout d'arguments de déstructuration et de «propagation» dans ES6, la syntaxe devient vraiment agréable. La méthode «propagation» est un palliatif.
Kris Kowal

Eh bien, vous pouvez toujours return Promise.of(x, y)au lieu d'une valeur scalaire du thenrappel.
Bergi

2

Voici une solution CoffeeScript.

Je cherchais la même solution et j'ai trouvé quelque chose de très intéressant dans cette réponse: Rejeter les promesses avec plusieurs arguments (comme $ http) dans AngularJS

la réponse de ce mec Florian

promise = deferred.promise

promise.success = (fn) ->
  promise.then (data) ->
   fn(data.payload, data.status, {additional: 42})
  return promise

promise.error = (fn) ->
  promise.then null, (err) ->
    fn(err)
  return promise

return promise 

Et pour l'utiliser:

service.get().success (arg1, arg2, arg3) ->
    # => arg1 is data.payload, arg2 is data.status, arg3 is the additional object
service.get().error (err) ->
    # => err

Devrait ->être =>?
SherylHohman

1
@SherylHohman Il y a quelques jours en 2015, cela a été écrit avec CoffeeScript ( coffeescript.org/#introduction ) et non avec la syntaxe ES6. Les flèches simples étaient des fonctions simples et les flèches grasses sont presque les mêmes que ES6 (je suppose que les flèches grasses ES6 ont été plus ou moins empruntées à CoffeScript).
Val Entin

@SherylHohman N'hésitez pas à modifier l'article dans ECMA si vous le souhaitez.
Val Entin

Merci pour votre réponse. Je modifierai uniquement pour préciser qu'il s'agit d'une solution de script de café. Avec cela, votre réponse est telle quelle et peut être utile pour les bases de code CoffeeScript. Merci pour votre offre d'édition, cependant: 1) Je ne suis pas assez familier avec CoffeeScript pour risquer d'éditer / casser votre solution ;-). 2) La traduction de votre code en JS moderne doit être considérée comme un écart par rapport à «l'intention d'origine de votre réponse», et ne doit donc pas passer un examen «éditer». Quelqu'un pourrait plutôt publier une nouvelle réponse, s'il le souhaite, traduisant votre code. Idéalement, ils
renverraient

0

Excellente question et excellente réponse de Benjamin, Kris et al - merci beaucoup!

J'utilise ceci dans un projet et j'ai créé un module basé sur le code de Benjamin Gruenwald . Il est disponible sur npmjs:

npm i -S promise-spread

Ensuite, dans votre code, faites

require('promise-spread');

Si vous utilisez une bibliothèque telle que any-promise

var Promise = require('any-promise');
require('promise-spread')(Promise);

Peut-être que d'autres trouvent cela utile aussi!


0

L'affectation de déstructuration dans ES6 serait utile ici.

let [arg1, arg2] = new Promise((resolve, reject) => {
    resolve([argument1, argument2]);
});

0

Étant donné que les fonctions en Javascript peuvent être appelées avec n'importe quel nombre d'arguments et que le document ne place aucune restriction sur les onFulfilled()arguments de la méthode en plus de la clause ci-dessous, je pense que vous pouvez passer plusieurs arguments à la onFulfilled()méthode tant que la valeur de la promesse est le premier argument.

2.2.2.1 il doit être appelé après que la promesse est accomplie, avec la valeur de la promesse comme premier argument.


-1

Pour citer l'article ci-dessous, "" puis "prend deux arguments, un rappel pour un cas de réussite et un autre pour le cas d'échec. Les deux sont facultatifs, vous pouvez donc ajouter un rappel pour le cas de réussite ou d'échec uniquement."

Je regarde généralement cette page pour toute question de base sur les promesses, faites-moi savoir si je me trompe

http://www.html5rocks.com/en/tutorials/es6/promises/


1
C'est incorrect, new Promisea la syntaxe function(resolve, error)tout thena la syntaxe.then(function(arg) {
megawac

2
@megawac c'est en fait juste mal mis - puis accepte deux (parfois 3) arguments - c'est juste plutôt rare
Benjamin Gruenbaum

@BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac

2
Oui, si vous lisez attentivement, c'est ce que prétend cette réponse - pas très utile dans le contexte de cette question mais pas incorrect.
Benjamin Gruenbaum
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.