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

127

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

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

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

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

получил бы оба arg1и arg2?

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

badunk
источник
В качестве подсказки я обнаружил, что использование github.com/then/promise (которое является реализацией barebones) показывает, что на самом деле он не предоставляет второй аргумент
badunk
2
Вы хотите использовать Bluebird с. Spread. - Кроме того, перестаньте заботиться о спецификации, спецификация - это взаимодействие между реализациями и минимальна по дизайну.
Бенджамин Грюнбаум

Ответы:

130

Я следую спецификации здесь и не уверен, позволяет ли она вызывать 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);    
});

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

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

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

let [a, b, c] = await Promise.resolve(['hello', 'world', '!']);
Бенджамин Грюнбаум
источник
2
Обратите внимание, что другие библиотеки (например, Q) также поддерживают, .spreadнапример Bluebird - причина, по которой этого нет в спецификации, заключается в том, что сохранение минимальной спецификации действительно имеет большое значение для обеспечения взаимодействия между кодом и библиотеками.
Бенджамин Грюнбаум
Второе замечание - вы можете вызвать Promise.allмассив перед применением функции, а не просто использовать .thenего для обработки некоторых библиотек сахара. Это не обязательно, но мило.
Бенджамин Грюнбаум
1
Promies.all является обязательным в вашей реализации, хотя вы можете просто изменить реализацию наreturn Promise.all(args).then(function(args){return fn.apply(this, args);})
Esailija 01
14
spreadэто временная мера. ES6 вводит деструктуризацию и оператор остатка / спреда, которые полностью устраняют необходимость spread. .then(([a, b, c]) => {})
Kris Kowal
3
@KrisKowal Обратите внимание , что .spread () неявно делает .all () , но синтаксис деструктурирующий ES6 не -> bluebirdjs.com/docs/api/spread.html
Gomino
66

Вы можете использовать деструктуризацию 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]) => {
    // ....
});
Wookiem
источник
3
Пример был бы хорош и полезен с этим ответом!
Рахул Верма
19

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

Esailija
источник
4

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

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

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

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

Я искал то же решение и нашел в этом ответе что-то очень интересное: отклонение обещаний с несколькими аргументами (например, $ http) в 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
Вал Энтин
источник
Должно ->быть =>?
Шерил Хохман
1
@SherylHohman Еще в 2015 году это было написано с использованием CoffeeScript ( coffeescript.org/#introduction ), а не синтаксиса ES6. Простая стрелка была простыми функциями, а толстые стрелки почти такие же, как ES6 (я думаю, толстые стрелки ES6 были более или менее заимствованы из CoffeScript).
Вал Энтин,
@SherylHohman Не стесняйтесь редактировать сообщение в ECMA, если хотите.
Вал Энтин,
Спасибо за ваш ответ. Отредактирую только, чтобы уточнить, что это решение кофейного скрипта. При этом ваш ответ остается неизменным и может быть полезен для баз кода CoffeeScript. Однако спасибо за ваше предложение отредактировать: 1) Я недостаточно знаком с CoffeeScript, чтобы рисковать редактировать / ломать ваше решение ;-). 2) Перевод вашего кода на современный JS следует рассматривать как отклонение от «первоначального намерения вашего ответа», поэтому он не должен проходить проверку «редактировать». Скорее, кто-то может опубликовать новый ответ, если так захочет, переведя ваш код. В идеале они должны ссылаться на ваш ответ как на источник вдохновения :-)
Шерил Хохман
0

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

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

npm i -S promise-spread

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

require('promise-spread');

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

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

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

AndreasPizsa
источник
0

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

let [arg1, arg2] = new Promise((resolve, reject) => {
    resolve([argument1, argument2]);
});
Рави Теджа
источник
0

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

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

Jazzepi
источник
-1

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

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

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

Михаил Вознесенский
источник
1
Это неверно, new Promiseимеет синтаксис, function(resolve, error)но thenесть синтаксис.then(function(arg) {
megawac
2
@megawac это на самом деле правильно, просто неправильно поставлено - затем принимает два (иногда 3) аргумента - это просто довольно необычно
Бенджамин Грюнбаум
@BenjaminGruenbaum afaik its.then(function(/*resolve args*/){/*resolve handler*/}, function(/*reject args*/){/*reject handler*/})
megawac
2
Да, если вы внимательно прочитаете, это то, что утверждает этот ответ - не очень полезно в контексте этого вопроса, но не неверно.
Бенджамин Грюнбаум