Czy obietnice mogą mieć wiele argumentów do wypełnienia?

127

Postępuję zgodnie ze specyfikacją tutaj i nie jestem pewien, czy pozwala ona na wywołanie onFulfilled z wieloma argumentami. Na przykład:

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

taki, że mój kod:

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

otrzyma oba arg1i arg2?

Nie obchodzi mnie, jak robi to jakakolwiek konkretna realizacja obietnic, chciałbym ściśle przestrzegać specyfikacji w3c dotyczących obietnic.

badunk
źródło
Jako wskazówkę stwierdziłem, że użycie github.com/then/promise (która jest implementacją barebones) pokazuje, że w rzeczywistości nie dostarcza drugiego argumentu
badunk
2
Chcesz używać Bluebird z rozszerzeniem .spread. - przestań też przejmować się specyfikacją, specyfikacja dotyczy interakcji między implementacjami i jest minimalna z założenia.
Benjamin Gruenbaum

Odpowiedzi:

130

Postępuję zgodnie ze specyfikacją tutaj i nie jestem pewien, czy pozwala ona na wywołanie onFulfilled z wieloma argumentami.

Nie, tylko pierwszy parametr będzie traktowany jako wartość rozdzielczości w konstruktorze obietnicy. Możesz rozwiązać za pomocą wartości złożonej, takiej jak obiekt lub tablica.

Nie obchodzi mnie, jak robi to jakakolwiek konkretna realizacja obietnic, chciałbym ściśle przestrzegać specyfikacji w3c dotyczących obietnic.

Myślę, że właśnie tam się mylisz. Specyfikacja została zaprojektowana jako minimalna i została stworzona do współdziałania między bibliotekami obietnic. Chodzi o to, aby mieć podzbiór, który na przykład DOM futures może niezawodnie wykorzystywać, a biblioteki mogą wykorzystywać. Implementacje Promise .spreadod jakiegoś czasu robią to, o co prosisz . Na przykład:

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

Z Bluebird . Jednym z rozwiązań, jeśli chcesz mieć tę funkcjonalność, jest wypełnienie jej.

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

Dzięki temu możesz:

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

Z rodzimymi obietnicami swobodnie bawią się . Lub użyj spreadu, który jest teraz (2018) powszechny w przeglądarkach:

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

Lub z oczekiwaniem:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
Benjamin Gruenbaum
źródło
2
Zauważ, że inne biblioteki (takie jak Q) również obsługują, .spreadjak Bluebird - powodem, dla którego nie ma tego w specyfikacji, jest to, że utrzymanie minimalnej specyfikacji jest naprawdę poważną sprawą , aby umożliwić współdziałanie między kodem a bibliotekami.
Benjamin Gruenbaum
Druga uwaga - możesz chcieć wywołać Promise.alltablicę przed zastosowaniem funkcji, zamiast tylko .thenobsługiwać niektóre biblioteki cukru. To nie jest obowiązkowe, ale jest urocze.
Benjamin Gruenbaum
1
Promies.all jest obowiązkowe w twojej implementacji, chociaż możesz po prostu zmienić implementację nareturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija
14
spreadjest chwilową luką. ES6 wprowadza destrukturyzację i operatora spoczynku / rozproszenia, co eliminuje potrzebę spreadbezpośredniego. .then(([a, b, c]) => {})
Kris Kowal
3
@KrisKowal Zauważ, że .spread () pośrednio robi .all (), ale składnia destrukturyzacji ES6 nie -> bluebirdjs.com/docs/api/spread.html
Gomino
66

Możesz użyć destrukturyzacji E6:

Destrukturyzacja obiektów:

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

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

Destrukturyzacja macierzy:

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

promise.then(([arg1, arg2]) => {
    // ....
});
Wookiem
źródło
3
Przykład byłby miły i pomocny z tą odpowiedzią!
Rahul Verma
19

Wartość spełnienia obietnicy jest analogiczna do wartości zwracanej funkcji, a przyczyna odrzucenia obietnicy jest analogiczna do rzuconego wyjątku funkcji. Funkcje nie mogą zwracać wielu wartości, więc obietnice nie mogą mieć więcej niż 1 wartość realizacji.

Esailija
źródło
4

O ile wiem, czytając specyfikację ES6 Promise i standardową specyfikację obietnicy, nie ma klauzuli uniemożliwiającej implementacji obsługę tego przypadku - jednak nie jest ona zaimplementowana w następujących bibliotekach:

Zakładam, że powodem pomijania rozwiązań wieloskładnikowych jest sprawienie, aby zmiana kolejności była bardziej zwięzła (tj. Ponieważ można zwrócić tylko jedną wartość w funkcji, sprawiłoby to, że przepływ sterowania byłby mniej intuicyjny) Przykład:

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
źródło
8
Q nie obsługuje rozdzielczości wielowartościowych, ponieważ obietnice służą jako proxy dla wyniku wywołania funkcji, ale mogą również służyć jako proxy dla obiektów zdalnych. W obu tych przypadkach tablica jest jedyną rozsądną reprezentacją wartości złożonej. Dzięki dodaniu destrukturyzacji i argumentów „rozprzestrzeniania” w ES6 składnia staje się naprawdę ładna. Metoda „spreadu” jest chwilową luką.
Kris Kowal
Cóż, zawsze możesz return Promise.of(x, y)zamiast wartości skalarnej z thenwywołania zwrotnego.
Bergi
2

Oto rozwiązanie CoffeeScript.

Szukałem tego samego rozwiązania i znalazłem coś bardzo interesującego w tej odpowiedzi: Odrzucanie obietnic z wieloma argumentami (np. $ Http) w AngularJS

odpowiedź tego gościa Floriana

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 

I aby z niego skorzystać:

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
źródło
Powinien ->być =>?
SherylHohman
1
@SherylHohman Dawno temu w 2015 roku było to napisane przy użyciu CoffeeScript ( coffeescript.org/#introduction ), a nie składni ES6. Prosta strzałka była prostymi funkcjami, a grube strzałki są prawie takie same jak w ES6 (myślę, że grube strzałki w ES6 zostały mniej więcej zapożyczone z CoffeScript).
Val Entin
@SherylHohman Jeśli chcesz, możesz edytować post w ECMA.
Val Entin
Dzięki za twoją odpowiedź. Zmienię tylko po to, aby wyjaśnić, że jest to rozwiązanie dotyczące scenariusza kawy. Dzięki temu Twoja odpowiedź jest taka, jaka jest i może być przydatna dla baz kodu CoffeeScript. Dziękuję jednak za propozycję edycji: 1) Nie znam CoffeeScript na tyle, aby ryzykować edycję / zepsucie rozwiązania ;-). 2) Tłumaczenie twojego kodu do nowoczesnego JS powinno być traktowane jako odchylenie od „pierwotnej intencji twojej odpowiedzi”, dlatego nie powinno przechodzić „edycji” Recenzji. Zamiast tego ktoś mógłby zamieścić nową odpowiedź, jeśli jest taka skłonna, tłumacząc twój kod. Idealnie byłoby, gdyby jako inspirację
podali
0

Świetne pytanie i świetna odpowiedź od Benjamina, Krisa i innych - wielkie dzięki!

Używam tego w projekcie i stworzyłem moduł oparty na kodzie Benjamina Gruenwalda . Jest dostępny na npmjs:

npm i -S promise-spread

Następnie w swoim kodzie zrób

require('promise-spread');

Jeśli używasz biblioteki, takiej jak any-promise

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

Może inni też uznają to za przydatne!

AndreasPizsa
źródło
0

Pomogłaby tutaj dezstrukturyzacja przypisania w ES6. Na przykład:

let [arg1, arg2] = new Promise((resolve, reject) => {
    resolve([argument1, argument2]);
});
Ravi Teja
źródło
0

Ponieważ funkcje w Javascript można wywołać z dowolną liczbą argumentów, a dokument nie nakłada żadnych ograniczeń na onFulfilled()argumenty metody poza poniższą klauzulą, myślę, że można przekazać do onFulfilled()metody wiele argumentów, o ile wartość obietnicy jest równa pierwszy argument.

2.2.2.1 należy go nazwać po spełnieniu obietnicy, z wartością obietnicy jako pierwszym argumentem.

Jazzepi
źródło
-1

Aby zacytować poniższy artykuł, "" to "przyjmuje dwa argumenty, wywołanie zwrotne dla przypadku powodzenia i inny dla przypadku niepowodzenia. Oba są opcjonalne, więc możesz dodać wywołanie zwrotne tylko dla przypadku sukcesu lub niepowodzenia."

Zwykle zaglądam na tę stronę w poszukiwaniu podstawowych pytań dotyczących obietnic, daj mi znać, jeśli się mylę

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

Michael Voznesensky
źródło
1
To niepoprawne, new Promisema składnię, function(resolve, error)podczas gdy thenma składnię.then(function(arg) {
megawac
2
@megawac jest właściwie poprawne, tylko źle podane - następnie akceptuje dwa (czasami 3) argumenty - to po prostu raczej rzadkie
Benjamin Gruenbaum
@BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac
2
Tak, jeśli uważnie czytasz, to właśnie twierdzi ta odpowiedź - niezbyt przydatna w kontekście tego pytania, ale nie jest niepoprawna.
Benjamin Gruenbaum