As promessas podem ter vários argumentos para onFulfilled?

127

Estou seguindo as especificações aqui e não tenho certeza se permite que onFulfilled seja chamado com vários argumentos. Por exemplo:

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

tal que meu código:

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

receberia ambos arg1e arg2?

Não me importo com o modo como qualquer implementação específica de promessas faz isso, desejo seguir de perto as especificações do w3c.

badunk
fonte
Como sugestão, descobri que o uso de github.com/then/promise (que é uma implementação de barebones) mostra que ele realmente não fornece o segundo argumento
badunk
2
Você deseja usar o Bluebird com .spread. - também, pare de se preocupar com as especificações, as especificações são sobre interoperabilidade entre implementações e são mínimas por design.
Benjamin Gruenbaum

Respostas:

130

Estou seguindo as especificações aqui e não tenho certeza se permite que onFulfilled seja chamado com vários argumentos.

Não, apenas o primeiro parâmetro será tratado como valor de resolução no construtor da promessa. Você pode resolver com um valor composto como um objeto ou matriz.

Não me importo com o modo como qualquer implementação específica de promessas faz isso, desejo seguir de perto as especificações do w3c.

É aí que eu acredito que você está errado. A especificação foi projetada para ser mínima e foi criada para interoperar entre bibliotecas de promessas. A idéia é ter um subconjunto que os futuros do DOM, por exemplo, possam usar com segurança e as bibliotecas possam consumir. As implementações promissoras fazem o que você pede .spreadhá um tempo. Por exemplo:

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

Com Bluebird . Uma solução, se você deseja essa funcionalidade, é polifillá-la.

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

Isso permite que você faça:

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

Com promessas nativas à vontade, brinque . Ou use propagação que é agora (2018) comum nos navegadores:

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

Ou com aguardar:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
Benjamin Gruenbaum
fonte
2
Note que outras bibliotecas (como Q) também suportam .spreadcomo Bluebird - a razão pela qual não está na especificação é que manter a especificação mínima é realmente um grande negócio para permitir a interoperabilidade entre código e bibliotecas.
Benjamin Gruenbaum
Segunda observação - convém chamar Promise.alla matriz antes de aplicar a função, em vez de apenas .thenutilizá-la para lidar com algumas bibliotecas de açúcar fornecidas. Não é obrigatório, mas é fofo.
Benjamin Gruenbaum
1
Promies.all é obrigatório com a sua implementação, embora você poderia apenas alterar a implementação dereturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija
14
spreadé uma pechincha. O ES6 introduz a desestruturação e o operador de descanso / propagação, o que elimina a necessidade spreadimediata. .then(([a, b, c]) => {})
Kris Kowal
3
@KrisKowal Observe que .spread () implica implicitamente .all (), mas a sintaxe de desestruturação do ES6 não -> bluebirdjs.com/docs/api/spread.html
Gomino
66

Você pode usar a desestruturação E6:

Destruição de objetos:

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

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

Destruição da matriz:

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

promise.then(([arg1, arg2]) => {
    // ....
});
Wookiem
fonte
3
Um exemplo seria agradável e útil com esta resposta!
Rahul Verma
19

O valor de cumprimento de uma promessa é paralelo ao valor de retorno de uma função e o motivo de rejeição de uma promessa é paralelo à exceção lançada de uma função. As funções não podem retornar vários valores, portanto, as promessas não devem ter mais de 1 valor de cumprimento.

Esailija
fonte
4

Tanto quanto posso dizer, lendo a especificação ES6 Promise e a especificação padrão de promessa, não há nenhuma cláusula que impeça uma implementação de lidar com esse caso - no entanto, ela não é implementada nas seguintes bibliotecas:

Suponho que a razão pela qual eles omitem as resoluções com vários argumentos é tornar a ordem de mudança mais sucinta (ou seja, como você pode retornar apenas um valor em uma função, isso tornaria o fluxo de controle menos intuitivo).

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
fonte
8
Q não suporta resoluções com vários valores porque as promessas servem como proxies para o resultado de uma chamada de função, mas também podem proxy para objetos remotos. Nos dois casos, uma matriz é a única representação sensível de um valor composto. Com a adição de argumentos de desestruturação e "disseminação" no ES6, a sintaxe fica realmente agradável. O método "spread" é ​​um paliativo.
Kris Kowal
Bem, você sempre pode, em return Promise.of(x, y)vez de um valor escalar, do thenretorno de chamada.
Bergi
2

Aqui está uma solução CoffeeScript.

Eu estava procurando a mesma solução e achei algo muito interessante nesta resposta: Rejeitar promessas com vários argumentos (como $ http) no AngularJS

a resposta desse cara 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 para usá-lo:

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
fonte
Deveria ->ser =>?
SherylHohman
1
@SherylHohman Nos dias de 2015, isso foi escrito com CoffeeScript ( coffeescript.org/#introduction ) e não com a sintaxe ES6. Seta simples era funções simples e setas gordas são quase as mesmas do ES6 (acho que as setas gordas ES6 foram mais ou menos emprestadas do CoffeScript).
Val Entin
@SherylHohman Sinta-se livre para editar a postagem no ECMA, se desejar.
Val Entin
Obrigado pela sua resposta. Editarei apenas para esclarecer que esta é uma solução de script para café. Com isso, sua resposta permanece como está e pode ser útil para as bases de código CoffeeScript. Obrigado pela sua oferta de edição, no entanto: 1) Não estou familiarizado o suficiente com o CoffeeScript para arriscar editar / interromper sua solução ;-). 2) A tradução do seu código para o JS moderno deve ser considerada um desvio da "intenção original da sua resposta", portanto não deve passar por uma revisão de "edição". Em vez disso, alguém pode postar uma nova resposta, se houver, traduzindo seu código. Idealmente, eles ligariam de volta à sua resposta como inspiração :-)
SherylHohman
0

Ótima pergunta e ótima resposta de Benjamin, Kris, et al - muito obrigado!

Estou usando isso em um projeto e criei um módulo baseado no código de Benjamin Gruenwald . Está disponível no npmjs:

npm i -S promise-spread

Então, no seu código, faça

require('promise-spread');

Se você estiver usando uma biblioteca como any-promise

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

Talvez outros achem isso útil também!

AndreasPizsa
fonte
0

A desestruturação da atribuição no ES6 ajudaria aqui.

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

Como as funções em Javascript podem ser chamadas com qualquer número de argumentos, e o documento não impõe nenhuma restrição aos onFulfilled()argumentos do método além da cláusula abaixo, acho que você pode passar vários argumentos para o onFulfilled()método desde que o valor da promessa seja o primeiro argumento.

2.2.2.1 deve ser chamado após a promessa ser cumprida, com o valor da promessa como seu primeiro argumento.

Jazzepi
fonte
-1

Para citar o artigo abaixo, "" then "recebe dois argumentos, um retorno de chamada para um caso de sucesso e outro para o caso de falha. Ambos são opcionais, portanto, você pode adicionar um retorno de chamada apenas para o caso de sucesso ou falha."

Normalmente, olho para esta página todas as perguntas básicas sobre promessas, deixe-me saber se estou errado

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

Michael Voznesensky
fonte
1
Isso é incorreto, new Promisetem a sintaxe function(resolve, error)enquanto thentem a sintaxe.then(function(arg) {
megawac 31/03
2
@megawac é realmente correto apenas mal colocada - então aceita dois (às vezes 3) argumentos - é apenas bastante incomum
Benjamin Gruenbaum
@BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac
2
Sim, se você ler atentamente, é isso que esta resposta está reivindicando - não é muito útil no contexto desta pergunta, mas não está incorreta.
Benjamin Gruenbaum