Могут ли промисы иметь несколько аргументов для onFulfilled?

Я следую спецификации здесь и не уверен, позволяет ли она вызывать onFulfilled с несколькими аргументами. . Например:

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

так что мой код:

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

получит и arg1, и arg2?

Меня не волнует, как это делает какая-либо конкретная реализация обещаний, я хочу внимательно следить за спецификацией w3c для обещаний.


person badunk    schedule 31.03.2014    source источник
comment
В качестве подсказки я обнаружил, что с помощью github.com/then/promise (это базовая реализация) показывает, что на самом деле он не предоставляет 2-й аргумент   -  person badunk    schedule 01.04.2014
comment
Вы хотите использовать Bluebird с расширением .spread. - кроме того, перестаньте заботиться о спецификации, спецификация полностью посвящена взаимодействию между реализациями и минимальна по дизайну.   -  person Benjamin Gruenbaum    schedule 01.04.2014


Ответы (9)


Я следую спецификации здесь и не уверен, позволяет ли она вызывать onFulfilled с несколькими аргументами.

Нет, только первый параметр будет рассматриваться как значение разрешения в конструкторе промисов. Вы можете разрешить составное значение, такое как объект или массив.

Меня не волнует, как это делает какая-либо конкретная реализация обещаний, я хочу внимательно следить за спецификацией w3c для обещаний.

Вот где я считаю, что вы не правы. Спецификация разработана так, чтобы быть минимальной и предназначена для взаимодействия между библиотеками промисов. Идея состоит в том, чтобы иметь подмножество, которое, например, DOM-фьючерсы могут надежно использовать, а библиотеки могут потреблять. Реализации обещаний делают то, что вы просите с помощью .spread на некоторое время. Например:

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

С помощью Bluebird. Одним из решений, если вам нужна эта функциональность, является ее полифилл.

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

Это позволяет вам:

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

С нативными промисами легко fiddle. Или используйте спред, который сейчас (2018 г.) является обычным явлением в браузерах:

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

Или с ожиданием:

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
person Benjamin Gruenbaum    schedule 01.04.2014
comment
Обратите внимание, что другие библиотеки (например, Q) также поддерживают .spread, например Bluebird. Причина, по которой этого нет в спецификации, заключается в том, что минимизация спецификации действительно важна для обеспечения взаимодействия между кодом и библиотеками. - person Benjamin Gruenbaum; 01.04.2014
comment
Второе примечание: вы можете вызвать Promise.all для массива перед применением функции, а не просто .then для обработки некоторых сахарных библиотек. Это не обязательно, но мило. - person Benjamin Gruenbaum; 01.04.2014
comment
Promies.all является обязательным для вашей реализации, хотя вы можете просто изменить реализацию на return Promise.all(args).then(function(args){return fn.apply(this, args);}) - person Esailija; 01.04.2014
comment
Да, именно это и должен был сказать второй комментарий :) Для того, чтобы имитировать то, что делают библиотеки. - person Benjamin Gruenbaum; 01.04.2014
comment
spread — временная мера. ES6 вводит деструктурирование и оператор rest/spread, которые полностью устраняют необходимость в spread. .then(([a, b, c]) => {}) - person Kris Kowal; 02.04.2014
comment
Я думаю, вы предполагаете, что я потребляю обещания, и поэтому решение состоит в том, что я могу использовать библиотеку обещаний для решения моей проблемы. Я пытаюсь создать библиотеку, чтобы определить, могу ли я разрешить (arg1, arg2) и позволить потребителям библиотеки получать оба аргумента как часть спецификации. - person badunk; 03.04.2014
comment
@KrisKowal Обратите внимание, что .spread() неявно выполняет .all(), но синтаксис деструктурирования ES6 этого не делает -› bluebirdjs.com/docs/api/spread.html - person Gomino; 06.12.2016
comment
@BenjaminGruenbaum Использовать не то же самое: Promise.all(["Hello","World","!"]).then(([a,b,c]) => { console.log(a,b+c); }); ??? - person robe007; 17.09.2018
comment
@BenjaminGruenbaum Еще один момент: если я сделаю: Promise.resolve([anAsyncFunction(),"World","!"]).then(([a,b,c]) => { console.log(a,b+c); });, это вернет: Promise { <pending> } 'World !' - person robe007; 18.09.2018

Вы можете использовать деструктурирование E6:

Деструктуризация объекта:

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

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

Деструктуризация массива:

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

promise.then(([arg1, arg2]) => {
    // ....
});
person Wookiem    schedule 29.12.2016
comment
Пример был бы хорошим и полезным с этим ответом! - person vrintle; 12.11.2018

Значение выполнения обещания соответствует возвращаемому значению функции, а причина отклонения обещания соответствует выданному исключению функции. Функции не могут возвращать несколько значений, поэтому промисы не должны иметь более одного значения выполнения.

person Esailija    schedule 01.04.2014

Насколько я могу судить по спецификации ES6 Promise и стандартная спецификация промиса нет пункта, запрещающего реализации обрабатывать этот случай, однако он не реализован в следующие библиотеки:

Я предполагаю, что причина, по которой они опускают разрешения с несколькими аргументами, заключается в том, чтобы сделать порядок изменения более кратким (т. Е. Поскольку вы можете вернуть только одно значение в функции, это сделает поток управления менее интуитивным). Пример:

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);
});
person megawac    schedule 01.04.2014
comment
Q не поддерживает разрешения с несколькими значениями, потому что промисы служат прокси для результата вызова функции, но также могут прокси для удаленных объектов. В обоих этих случаях массив является единственным разумным представлением составного значения. С добавлением аргументов деструктурирования и «распространения» в ES6 синтаксис становится действительно приятным. Метод «спреда» является временным решением. - person Kris Kowal; 02.04.2014
comment
Ну, вы всегда можете return Promise.of(x, y) вместо скалярного значения из обратного вызова then. - person Bergi; 26.07.2014

Здесь поможет деструктурирование назначения в ES6. Например:

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

Вот решение CoffeeScript.

Я искал то же решение и нашел что-то очень интересное в этом ответе: https://stackoverflow.com/questions/17686612/rejecting-promises-with-multiple-arguments-like-http-in-angularjs

ответ этого парня Флориан

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 

И использовать его:

service.get().success (arg1, arg2, arg3) ->
    # => arg1 is data.payload, arg2 is data.status, arg3 is the additional object
service.get().error (err) ->
    # => err
person Val Entin    schedule 08.07.2015
comment
Должен ли -> быть => ? - person SherylHohman; 03.05.2020
comment
@SherylHohman Еще в 2015 году это было написано с помощью CoffeeScript (coffeescript.org/#introduction), а не ES6. синтаксис. Простая стрелка была простыми функциями, а толстые стрелки почти такие же, как в ES6 (я думаю, толстые стрелки ES6 были более или менее заимствованы из CoffeScript). - person Val Entin; 06.05.2020
comment
@SherylHohman Не стесняйтесь редактировать сообщение в ECMA, если хотите. - person Val Entin; 06.05.2020
comment
Спасибо за ваш ответ. Я отредактирую только для того, чтобы уточнить, что это решение для сценария кофе. При этом ваш ответ остается без изменений и может быть полезен для кодовых баз CoffeeScript. Однако спасибо за ваше предложение отредактировать: 1) Я недостаточно знаком с CoffeeScript, чтобы рисковать редактировать/сломать ваше решение ;-). 2) Перевод вашего кода на современный JS следует рассматривать как отклонение от первоначального замысла вашего ответа, поэтому он не должен проходить проверку «редактирования». Скорее, кто-то может опубликовать новый ответ, если он захочет, переводя ваш код. В идеале они ссылались бы на ваш ответ как на источник вдохновения :-) - person SherylHohman; 06.05.2020

Отличный вопрос и отличный ответ Бенджамина, Криса и др. - большое спасибо!

Я использую это в проекте и создал модуль на основе кода Бенджамина Грюнвальда. Он доступен на npmjs:

npm i -S promise-spread

Затем в вашем коде сделайте

require('promise-spread');

Если вы используете такую ​​библиотеку, как any-promise

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

Может быть, другие тоже найдут это полезным!

person AndreasPizsa    schedule 04.02.2016

Поскольку функции в Javascript можно вызывать с любым количеством аргументов, а документ не накладывает никаких ограничений на аргументы метода onFulfilled(), кроме приведенного ниже предложения, я думаю, что вы можете передать несколько аргументов методу onFulfilled(), если значение обещания это первый аргумент.

2.2.2.1 он должен вызываться после выполнения обещания со значением обещания в качестве первого аргумента.

person Jazzepi    schedule 31.03.2014

Чтобы процитировать приведенную ниже статью, ««then» принимает два аргумента: обратный вызов для случая успеха и еще один для случая отказа. Оба являются необязательными, поэтому вы можете добавить обратный вызов только для случая успеха или неудачи».

Я обычно просматриваю эту страницу для любых основных вопросов об обещаниях, дайте мне знать, если я ошибаюсь.

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

person Michael Voznesensky    schedule 31.03.2014
comment
Это неверно, new Promise имеет синтаксис function(resolve, error), а then имеет синтаксис .then(function(arg) { . - person megawac; 01.04.2014
comment
@megawac на самом деле правильно, просто плохо выразился - затем принимает два (иногда 3) аргумента - это довольно необычно - person Benjamin Gruenbaum; 01.04.2014
comment
@BenjaminGruenbaum на самом деле это .then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/}) - person megawac; 01.04.2014
comment
Да, если вы внимательно прочитаете, это то, что утверждает этот ответ - не очень полезно в контексте этого вопроса, но не неверно. - person Benjamin Gruenbaum; 01.04.2014