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.

badunk
la source
À 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', '!']);
Benjamin Gruenbaum
la source
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]) => {
    // ....
});
Wookiem
la source
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.

Esailija
la source
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);
});
mégawac
la source
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
Val Entin
la source
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!

AndreasPizsa
la source
0

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

let [arg1, arg2] = new Promise((resolve, reject) => {
    resolve([argument1, argument2]);
});
Ravi Teja
la source
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.

Jazzepi
la source
-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/

Michael Voznesensky
la source
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