Können Versprechen mehrere Argumente für onFulfilled haben?

127

Ich folge hier der Spezifikation und bin mir nicht sicher, ob onFulfilled mit mehreren Argumenten aufgerufen werden kann. Beispielsweise:

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

so dass mein Code:

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

würde beides erhalten arg1und arg2?

Es ist mir egal, wie eine bestimmte Versprechen-Implementierung funktioniert, ich möchte die w3c-Spezifikation für Versprechen genau befolgen.

Badunk
quelle
Als Hinweis fand ich, dass die Verwendung von github.com/then/promise (was eine Barebone-Implementierung ist) zeigt, dass es tatsächlich nicht das zweite Argument
liefert
2
Sie möchten Bluebird mit .spread verwenden. - Hören Sie auch auf, sich um die Spezifikation zu kümmern. Die Spezifikation dreht sich alles um die Interaktion zwischen Implementierungen und ist von Natur aus minimal.
Benjamin Gruenbaum

Antworten:

130

Ich folge hier der Spezifikation und bin mir nicht sicher, ob onFulfilled mit mehreren Argumenten aufgerufen werden kann.

Nein, nur der erste Parameter wird im Versprechen-Konstruktor als Auflösungswert behandelt. Sie können mit einem zusammengesetzten Wert wie einem Objekt oder Array auflösen.

Es ist mir egal, wie eine bestimmte Versprechen-Implementierung funktioniert, ich möchte die w3c-Spezifikation für Versprechen genau befolgen.

Da glaube ich, dass du falsch liegst. Die Spezifikation ist minimal ausgelegt und für die Zusammenarbeit zwischen Versprechensbibliotheken konzipiert. Die Idee ist, eine Teilmenge zu haben, die DOM-Futures beispielsweise zuverlässig nutzen und Bibliotheken nutzen können. Vielversprechende Implementierungen machen das, womit Sie schon seit einiger Zeit fragen .spread. Beispielsweise:

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

Mit Bluebird . Eine Lösung, wenn Sie diese Funktionalität wünschen, besteht darin, sie zu füllen.

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

So können Sie Folgendes tun:

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

Mit einheimischen Versprechungen beruhigen Geige . Oder verwenden Sie Spread, der heute (2018) in Browsern üblich ist:

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

Oder mit warten:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
Benjamin Gruenbaum
quelle
2
Beachten Sie, dass andere Bibliotheken (wie Q) ebenfalls .spreadBluebird unterstützen - der Grund, warum es nicht in der Spezifikation enthalten ist, ist, dass es wirklich eine große Sache ist, die Spezifikation minimal zu halten , um eine Interop zwischen Code und Bibliotheken zu ermöglichen.
Benjamin Gruenbaum
Zweiter Hinweis: Möglicherweise möchten Sie Promise.alldas Array aufrufen, bevor Sie die Funktion anwenden, anstatt sie nur .thenfür einige Zuckerbibliotheken zu verwenden. Es ist nicht obligatorisch, aber es ist süß.
Benjamin Gruenbaum
1
Promies.all ist obligatorisch für Ihre Implementierung, obwohl Sie die Implementierung einfach inreturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija
14
spreadist eine Notlösung. ES6 führt die Destrukturierung und den Rest / Spread-Operator ein, wodurch die Notwendigkeit einer spreaddirekten Beseitigung entfällt . .then(([a, b, c]) => {})
Kris Kowal
3
@KrisKowal Hinweis implizit , dass .spread () tut .all () , aber die Syntax ES6 Destrukturierung nicht -> bluebirdjs.com/docs/api/spread.html
Gomino
66

Sie können die E6-Destrukturierung verwenden:

Objektzerstörung:

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

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

Array-Destrukturierung:

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

promise.then(([arg1, arg2]) => {
    // ....
});
Wookiem
quelle
3
Ein Beispiel wäre nett und hilfreich bei dieser Antwort!
Rahul Verma
19

Der Erfüllungswert eines Versprechens entspricht dem Rückgabewert einer Funktion, und der Ablehnungsgrund eines Versprechens entspricht der ausgelösten Ausnahme einer Funktion. Funktionen können nicht mehrere Werte zurückgeben, daher dürfen Versprechen nicht mehr als einen Erfüllungswert haben.

Esailija
quelle
4

Soweit ich das Lesen der ES6-Versprechen-Spezifikation und der Standard-Versprechen-Spezifikation beurteilen kann, gibt es keine Klausel, die eine Implementierung daran hindert, diesen Fall zu behandeln - sie ist jedoch nicht in den folgenden Bibliotheken implementiert:

Ich gehe davon aus, dass der Grund dafür, dass sie Multi-Arg-Auflösungen weglassen, darin besteht, die Reihenfolge der Änderungen prägnanter zu gestalten (dh da Sie nur einen Wert in einer Funktion zurückgeben können, würde dies den Steuerungsfluss weniger intuitiv machen). Beispiel:

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);
});
Megawac
quelle
8
Q unterstützt keine mehrwertigen Auflösungen, da Versprechen als Proxys für das Ergebnis eines Funktionsaufrufs dienen, aber auch als Proxy für entfernte Objekte dienen können. In beiden Fällen ist ein Array die einzig sinnvolle Darstellung eines zusammengesetzten Werts. Mit der Hinzufügung von Destrukturierungs- und "Spread" -Argumenten in ES6 wird die Syntax wirklich gut. Die "Spread" -Methode ist eine Notlösung.
Kris Kowal
Nun, Sie könnten immer return Promise.of(x, y)anstelle eines skalaren Wertes aus dem thenRückruf.
Bergi
2

Hier ist eine CoffeeScript-Lösung.

Ich suchte nach der gleichen Lösung und fand etwas sehr Interessantes an dieser Antwort: Ablehnen von Versprechungen mit mehreren Argumenten (wie $ http) in AngularJS

die Antwort dieses Typen 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 

Und um es zu benutzen:

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
quelle
Sollte ->sein =>?
SherylHohman
1
@SherylHohman Damals im Jahr 2015 wurde dies mit CoffeeScript ( coffeescript.org/#introduction ) und nicht mit ES6-Syntax geschrieben. Einfacher Pfeil war einfache Funktionen und fette Pfeile sind fast die gleichen wie ES6 (ich denke, ES6 fette Pfeile wurden mehr oder weniger von CoffeScript entlehnt).
Val Entin
@SherylHohman Wenn Sie möchten, können Sie den Beitrag in ECMA bearbeiten.
Val Entin
Vielen Dank für Ihre Antwort. Ich werde nur bearbeiten, um zu verdeutlichen, dass dies eine Kaffeeskriptlösung ist. Damit bleibt Ihre Antwort unverändert und kann für CoffeeScript-Codebasen nützlich sein. Vielen Dank für Ihr Angebot zur Bearbeitung: 1) Ich bin mit CoffeeScript nicht vertraut genug, um das Risiko einzugehen, Ihre Lösung zu bearbeiten / zu beschädigen ;-). 2) Die Übersetzung Ihres Codes in modernes JS sollte als Abweichung von der "ursprünglichen Absicht Ihrer Antwort" betrachtet werden und sollte daher keine "Bearbeitungs" -Überprüfung bestehen. Vielmehr könnte jemand eine neue Antwort posten, wenn er dazu geneigt ist, und Ihren Code übersetzen. Im Idealfall würden sie als Inspiration auf Ihre Antwort
zurückgreifen
0

Tolle Frage und tolle Antwort von Benjamin, Kris ua - vielen Dank!

Ich verwende dies in einem Projekt und habe ein Modul erstellt, das auf dem Code von Benjamin Gruenwald basiert . Es ist auf npmjs verfügbar:

npm i -S promise-spread

Dann tun Sie es in Ihrem Code

require('promise-spread');

Wenn Sie eine Bibliothek wie verwenden any-promise

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

Vielleicht finden das auch andere nützlich!

AndreasPizsa
quelle
0

Eine De-Strukturierung der Zuweisung in ES6 würde hier helfen. Zum Beispiel:

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

Da Funktionen in Javascript mit einer beliebigen Anzahl von Argumenten aufgerufen werden können und das Dokument onFulfilled()die Argumente der Methode neben der folgenden Klausel nicht einschränkt , können Sie meiner Meinung nach mehrere Argumente an die onFulfilled()Methode übergeben, solange der Wert des Versprechens der ist erstes Argument.

2.2.2.1 Es muss aufgerufen werden, nachdem das Versprechen erfüllt wurde, wobei der Wert des Versprechens das erste Argument ist.

Jazzepi
quelle
-1

Um den folgenden Artikel zu zitieren, benötigt "" dann "zwei Argumente, einen Rückruf für einen Erfolgsfall und ein weiteres für den Fehlerfall. Beide sind optional, sodass Sie einen Rückruf nur für den Erfolgs- oder Fehlerfall hinzufügen können."

Normalerweise schaue ich auf dieser Seite nach grundlegenden Versprechungsfragen. Lassen Sie mich wissen, wenn ich falsch liege

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

Michael Voznesensky
quelle
1
Das ist falsch, new Promisehat die Syntax, function(resolve, error)während thendie Syntax.then(function(arg) {
Megawac
2
@ Megawac es ist eigentlich richtig nur schlecht ausgedrückt - dann akzeptiert zwei (manchmal 3) Argumente - es ist nur ziemlich ungewöhnlich
Benjamin Gruenbaum
@ BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
Megawac
2
Ja, wenn Sie genau lesen, ist es das, was diese Antwort behauptet - im Kontext dieser Frage nicht sehr nützlich, aber nicht falsch.
Benjamin Gruenbaum