Le promesse possono avere più argomenti su onFulfilled?


127

Sto seguendo le specifiche qui e non sono sicuro che consenta a onFulfilled di essere chiamato con più argomenti. Per esempio:

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

tale che il mio codice:

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

riceverebbe entrambi arg1earg2 ?

Non mi interessa come fa una specifica implementazione delle promesse, desidero seguire da vicino le specifiche w3c per le promesse.


Come suggerimento, ho scoperto che l'uso di github.com/then/promise (che è un'implementazione barebone) mostra che in realtà non fornisce il secondo argomento
badunk

2
Vuoi usare Bluebird con .spread. - Inoltre, smetti di preoccuparti delle specifiche, le specifiche riguardano l' interoperabilità tra le implementazioni ed è minima in base alla progettazione.
Benjamin Gruenbaum,

Risposte:


130

Sto seguendo le specifiche qui e non sono sicuro se consenta a onFulfilled di essere chiamato con più argomenti.

No, solo il primo parametro verrà trattato come valore di risoluzione nel costruttore della promessa. È possibile risolvere con un valore composito come un oggetto o un array.

Non mi interessa come fa una specifica implementazione delle promesse, desidero seguire da vicino le specifiche w3c per le promesse.

Ecco dove credo che tu abbia torto. Le specifiche sono progettate per essere minime e sono progettate per l'interoperabilità tra librerie promesse. L'idea è di avere un sottoinsieme che i futuri DOM possano ad esempio utilizzare in modo affidabile e che le librerie possano consumare. Le implementazioni promettenti fanno quello che chiedi.spread un po 'di tempo. Per esempio:

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

Con Bluebird . Una soluzione se si desidera questa funzionalità è quella di polifillarla.

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

Questo ti permette di fare:

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

Con promesse nativi a proprio agio violino . O usa spread che è ora (2018) banale nei browser:

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

O con wait:

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

2
Nota che anche altre librerie (come Q) supportano .spreadcome Bluebird - il motivo non è nelle specifiche è che mantenere le specifiche minime è davvero un grosso problema per consentire l'interoperabilità tra codice e librerie.
Benjamin Gruenbaum,

Seconda nota: è possibile che si desideri chiamare Promise.alll'array prima di applicare la funzione invece di .thenlimitarla a gestire alcune librerie di zucchero fornite. Non è obbligatorio, ma è carino.
Benjamin Gruenbaum,

1
Promies.all è obbligatorio con la tua implementazione, anche se potresti semplicemente cambiarla inreturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija

14
spreadè un punto fermo. ES6 introduce la destrutturazione e l'operatore di riposo / diffusione, che elimina la necessità di spreaddefinirla. .then(([a, b, c]) => {})
Kris Kowal,

3
@KrisKowal Nota che .spread () lo fa implicitamente .all () ma la sintassi destrutturante ES6 no -> bluebirdjs.com/docs/api/spread.html
Gomino

66

Puoi usare la destrutturazione E6:

Distruzione dell'oggetto:

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

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

Distrutturazione di array:

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

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

3
Un esempio sarebbe carino e utile con questa risposta!
Rahul Verma,

19

Il valore di adempimento di una promessa è parallelo al valore di ritorno di una funzione e la ragione del rifiuto di una promessa è parallela all'eccezione generata di una funzione. Le funzioni non possono restituire più valori, quindi le promesse non devono avere più di 1 valore di adempimento.


4

Per quanto ne so, leggendo la specifica Promessa ES6 e la specifica promessa standard non esiste alcuna clausola che impedisca a un'implementazione di gestire questo caso, tuttavia non è implementata nelle seguenti librerie:

Suppongo che la ragione per cui omettono le risoluzioni multi arg sia quella di rendere più succinto l'ordine di cambiamento (cioè, dato che si può restituire un solo valore in una funzione renderebbe il flusso di controllo meno intuitivo) Esempio:

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 non supporta risoluzioni multi-valore perché le promesse fungono da proxy per il risultato di una chiamata di funzione ma possono anche essere proxy per oggetti remoti. In entrambi questi casi, un array è l'unica rappresentazione sensata di un valore composto. Con l'aggiunta di argomenti di destrutturazione e "diffusione" in ES6, la sintassi diventa davvero piacevole. Il metodo "spread" è un punto fermo.
Kris Kowal,

Bene, potresti sempre return Promise.of(x, y)invece di un valore scalare dal thencallback.
Bergi,

2

Ecco una soluzione CoffeeScript.

Stavo cercando la stessa soluzione e ho trovato il seomething molto interessante da questa risposta: rifiutare le promesse con più argomenti (come $ http) in AngularJS

la risposta di questo ragazzo 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 

E per usarlo:

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

Dovrebbe ->essere =>?
SherylHohman,

1
@SherylHohman In passato nel 2015 questo è stato scritto con CoffeeScript ( coffeescript.org/#introduction ) e non con la sintassi ES6. La freccia semplice era funzioni semplici e le frecce grasse sono quasi le stesse di ES6 (immagino che le frecce grasse ES6 siano state più o meno prese in prestito da CoffeScript).
Val Entin

@SherylHohman Sentiti libero di modificare il post in ECMA, se lo desideri.
Val Entin

Grazie per la risposta. Modificherò solo per chiarire che questa è una soluzione di sceneggiatura del caffè. Con ciò, la tua risposta è così com'è e può essere utile per le basi di codice CoffeeScript. Grazie per la tua offerta di modifica, tuttavia: 1) Non ho abbastanza familiarità con CoffeeScript per rischiare di modificare / rompere la tua soluzione ;-). 2) La traduzione del tuo codice nel moderno JS dovrebbe essere considerata una deviazione dall'intento originale della tua risposta, quindi non dovrebbe passare una revisione 'modifica'. Piuttosto, qualcuno potrebbe pubblicare una nuova risposta, se così propenso, traducendo il tuo codice. Idealmente, ricollegherebbero alla tua risposta come fonte di ispirazione :-)
SherylHohman

0

Grande domanda e ottima risposta di Benjamin, Kris e altri - molte grazie!

Sto usando questo in un progetto e ho creato un modulo basato sul codice di Benjamin Gruenwald . È disponibile su npmjs:

npm i -S promise-spread

Quindi nel tuo codice, fallo

require('promise-spread');

Se stai utilizzando una libreria come any-promise

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

Forse anche altri lo trovano utile!


0

L'assegnazione della destrutturazione in ES6 sarebbe di aiuto in questo caso.

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

0

Poiché le funzioni in Javascript possono essere richiamate con qualsiasi numero di argomenti e il documento non pone alcuna restrizione agli onFulfilled()argomenti del metodo oltre alla clausola seguente, penso che sia possibile passare più argomenti al onFulfilled()metodo purché il valore della promessa sia il primo argomento.

2.2.2.1 deve essere chiamato dopo che la promessa è stata adempiuta, con il valore della promessa come primo argomento.


-1

Per citare l'articolo di seguito, "" allora "accetta due argomenti, un callback per un caso di successo e un altro per il caso di errore. Entrambi sono facoltativi, quindi è possibile aggiungere un callback solo per il caso di successo o fallimento."

Di solito cerco questa pagina per qualsiasi domanda di base sulla promessa, fammi sapere se sbaglio

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


1
Quello è errato, new Promiseha la sintassi function(resolve, error)mentre thenha la sintassi.then(function(arg) {
megawac

2
@megawac è in realtà corretto solo messo male - quindi accetta due (a volte 3) argomenti - è piuttosto insolito
Benjamin Gruenbaum

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

2
Sì, se leggi attentamente, questo è ciò che afferma questa risposta - non molto utile nel contesto di questa domanda ma non errato.
Benjamin Gruenbaum,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.