Can promises have multiple arguments to onFulfilled?

I'm following the spec here and I'm not sure whether it allows onFulfilled to be called with multiple arguments. For example:

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

such that my code:

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

would receive both arg1 and arg2?

I don't care about how any specific promises implementation does it, I wish to follow the w3c spec for promises closely.


Solution 1:

I'm following the spec here and I'm not sure whether it allows onFulfilled to be called with multiple arguments.

Nope, just the first parameter will be treated as resolution value in the promise constructor. You can resolve with a composite value like an object or array.

I don't care about how any specific promises implementation does it, I wish to follow the w3c spec for promises closely.

That's where I believe you're wrong. The specification is designed to be minimal and is built for interoperating between promise libraries. The idea is to have a subset which DOM futures for example can reliably use and libraries can consume. Promise implementations do what you ask with .spread for a while now. For example:

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

With Bluebird. One solution if you want this functionality is to polyfill it.

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

This lets you do:

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

With native promises at ease fiddle. Or use spread which is now (2018) commonplace in browsers:

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

Or with await:

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

Solution 2:

You can use E6 destructuring:

Object destructuring:

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

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

Array destructuring:

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

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

Solution 3:

The fulfillment value of a promise parallels the return value of a function and the rejection reason of a promise parallels the thrown exception of a function. Functions cannot return multiple values so promises must not have more than 1 fulfillment value.

Solution 4:

As far as I can tell reading the ES6 Promise specification and the standard promise specification theres no clause preventing an implementation from handling this case - however its not implemented in the following libraries:

  • RSVP.promise (#L516-544)
  • Q promise (#787)

I assume the reason for them omiting multi arg resolves is to make changing order more succinct (i.e. as you can only return one value in a function it would make the control flow less intuitive) Example:

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