¿Pueden las promesas tener múltiples argumentos para onFulfilled?

127

Estoy siguiendo la especificación aquí y no estoy seguro de si permite llamar a onFulfilled con múltiples argumentos. Por ejemplo:

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

tal que mi código:

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

recibiría ambos arg1y arg2?

No me importa cómo lo hace una implementación de promesas específicas, deseo seguir de cerca la especificación w3c para promesas.

badunk
fuente
Como pista, descubrí que el uso de github.com/then/promise (que es una implementación básica ) muestra que en realidad no proporciona el segundo argumento
badunk
2
Desea usar Bluebird con .spread. - también, deje de preocuparse por la especificación, la especificación se trata de interoperabilidad entre implementaciones y es mínima por diseño.
Benjamin Gruenbaum

Respuestas:

130

Estoy siguiendo la especificación aquí y no estoy seguro de si permite llamar a onFulfilled con múltiples argumentos.

No, solo el primer parámetro será tratado como valor de resolución en el constructor de promesa. Puede resolver con un valor compuesto como un objeto o matriz.

No me importa cómo lo hace una implementación de promesas específicas, deseo seguir de cerca la especificación w3c para promesas.

Ahí es donde creo que te equivocas. La especificación está diseñada para ser mínima y está diseñada para interoperar entre bibliotecas prometedoras. La idea es tener un subconjunto que los futuros de DOM, por ejemplo, puedan usar de manera confiable y las bibliotecas puedan consumir. Las implementaciones prometedoras hacen lo que pides .spreadpor un tiempo ahora. Por ejemplo:

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

Con Bluebird . Una solución si desea esta funcionalidad es rellenarla.

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

Esto te permite hacer:

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

Con promesas nativas a gusto violín . O use spread, que ahora es (2018) común en los navegadores:

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

O con esperar:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
Benjamin Gruenbaum
fuente
2
Tenga en cuenta que otras bibliotecas (como Q) también son compatibles .spreadcomo Bluebird; la razón por la que no está en la especificación es que mantener la especificación mínima es realmente un gran problema para permitir la interoperabilidad entre el código y las bibliotecas.
Benjamin Gruenbaum
Segunda nota: es posible que desee llamar Promise.alla la matriz antes de aplicar la función en lugar de solo .thenusarla para manejar algunas bibliotecas de azúcar. No es obligatorio, pero es lindo.
Benjamin Gruenbaum
1
Promies.all es obligatorio con su implementación, aunque podría cambiar la implementación areturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija
14
spreadEs un tapón. El ES6 introduce la desestructuración y el operador de reposo / dispersión, que eliminan la necesidad de un uso spreaddirecto. .then(([a, b, c]) => {})
Kris Kowal
3
@KrisKowal Tenga en cuenta que .spread () implícitamente hace .all () pero la sintaxis de desestructuración ES6 no -> bluebirdjs.com/docs/api/spread.html
Gomino
66

Puede usar la desestructuración E6:

Desestructuración de objetos:

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

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

Desestructuración de la matriz:

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

promise.then(([arg1, arg2]) => {
    // ....
});
Wookiem
fuente
3
¡Un ejemplo sería bueno y útil con esta respuesta!
Rahul Verma
19

El valor de cumplimiento de una promesa es paralelo al valor de retorno de una función y la razón de rechazo de una promesa es paralela a la excepción lanzada de una función. Las funciones no pueden devolver múltiples valores, por lo que las promesas no deben tener más de 1 valor de cumplimiento.

Esailija
fuente
4

Por lo que puedo decir, leer la especificación de Promesa ES6 y la especificación de promesa estándar no hay cláusula que impida que una implementación maneje este caso; sin embargo, no está implementado en las siguientes bibliotecas:

Supongo que la razón por la que omiten las resoluciones de múltiples argumentos es hacer que el orden de cambio sea más sucinto (es decir, como solo puede devolver un valor en una función, haría que el control fluya menos intuitivo) Ejemplo:

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
fuente
8
Q no admite resoluciones de valores múltiples porque las promesas sirven como representantes del resultado de una llamada a la función, pero también pueden representar los objetos remotos. En ambos casos, una matriz es la única representación sensible de un valor compuesto. Con la adición de argumentos de desestructuración y "difusión" en ES6, la sintaxis se vuelve realmente agradable. El método de "propagación" es un recurso provisional.
Kris Kowal
Bueno, siempre podría en return Promise.of(x, y)lugar de un valor escalar de la thendevolución de llamada.
Bergi
2

Aquí hay una solución CoffeeScript.

Estaba buscando la misma solución y encontré algo muy interesante de esta respuesta: Rechazar promesas con múltiples argumentos (como $ http) en AngularJS

la respuesta de este chico 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 

Y para 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
Val Entin
fuente
Debe ->ser =>?
SherylHohman
1
@SherylHohman En los días de 2015, esto se escribió con CoffeeScript ( coffeescript.org/#introduction ) y no con la sintaxis ES6. La flecha simple era funciones simples y las flechas gruesas son casi lo mismo que ES6 (supongo que las flechas gordas ES6 se tomaron más o menos prestadas de CoffeScript).
Val Entin
@SherylHohman Siéntase libre de editar la publicación en ECMA si lo desea.
Val Entin
Gracias por su respuesta. Solo editaré para aclarar que esta es una solución de script de café. Con eso, su respuesta es como es y puede ser útil para las bases de código de CoffeeScript. Sin embargo, gracias por su oferta para editar: 1) No estoy lo suficientemente familiarizado con CoffeeScript para arriesgarme a editar / romper su solución ;-). 2) La traducción de su código al JS moderno debe considerarse una desviación de la "intención original de su respuesta", por lo tanto, no debe pasar una revisión de "edición". Por el contrario, alguien podría publicar una nueva respuesta, si así lo desea, traduciendo su código. Idealmente, vincularían a su respuesta como inspiración :-)
SherylHohman
0

Gran pregunta y gran respuesta de Benjamin, Kris y otros. ¡Muchas gracias!

Estoy usando esto en un proyecto y he creado un módulo basado en el código de Benjamin Gruenwald . Está disponible en npmjs:

npm i -S promise-spread

Luego, en su código, haga

require('promise-spread');

Si está utilizando una biblioteca como any-promise

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

¡Quizás otros también encuentren esto útil!

AndreasPizsa
fuente
0

La desestructuración de la asignación en ES6 ayudaría aquí. Por ejemplo:

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

Dado que las funciones en Javascript se pueden invocar con cualquier número de argumentos, y el documento no impone ninguna restricción a los onFulfilled()argumentos del método además de la cláusula siguiente, creo que puede pasar múltiples argumentos al onFulfilled()método siempre que el valor de la promesa sea el primer argumento

2.2.2.1 debe llamarse después de que se cumpla la promesa, con el valor de la promesa como primer argumento.

Jazzepi
fuente
-1

Para citar el siguiente artículo, "" luego "toma dos argumentos, una devolución de llamada para un caso de éxito y otro para el caso de falla. Ambos son opcionales, por lo que puede agregar una devolución de llamada solo para el caso de éxito o falla".

Por lo general, busco en esta página cualquier pregunta básica sobre promesas, avíseme si estoy equivocado

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

Michael Voznesensky
fuente
1
Eso es incorrecto, new Promisetiene la sintaxis function(resolve, error)mientras que thentiene la sintaxis.then(function(arg) {
megawac
2
@megawac en realidad es correcto simplemente mal puesto - luego acepta dos (a veces 3) argumentos - es bastante poco común
Benjamin Gruenbaum
@BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac
2
Sí, si lees atentamente, eso es lo que dice esta respuesta: no es muy útil en el contexto de esta pregunta pero no es incorrecta.
Benjamin Gruenbaum