domenic / promises-unwrapping

The ES6 promises spec, as per September 2013 TC39 meeting
1.23k stars 94 forks source link

consider a version of Promise.all that works on object #41

Closed Raynos closed 10 years ago

Raynos commented 10 years ago

Promise.all takes an array of promises and returns a promise for an array.

This form of doing parallel execution is useful.

Another form of parallel execution that is useful is something that takes an object of key to promise and returns a promise for an object of key to value of promise at that key.

The majority of my parallel execution flow control is covered by a user land module called continuable-para that supports those two syntaxes. (the third syntax can be done with ES6 splat notation).

It would be a bit weird to have to use a 3rd party module for the object literal style parallel execution and use the browsers Promise.all for the array version.

domenic commented 10 years ago

The problems with such methods have been discussed at length in promise library repos, e.g. kriskowal/q#374, kriskowal/q#159, kriskowal/q#171, kriskowal/q#328. ES6 also makes them much less useful, generally, since you can do

Promise.all([pA, pB, pC]).then(([a, b, c]) => { ... })

instead of

Promise.whatever({ a: pA, b: pB, c: pC }).then(function (obj) { obj.a; obj.b; obj.c; });
Raynos commented 10 years ago

the discussion in those issues does not explain it well other then "slippery slope". If you take a look at the continuable-para implementation it's a simple three state machine

var list = require('continuable-list')
var hash = require('continuable-hash')

module.exports = function (obj, cb) {
  if(Array.isArray(obj))
    return list(obj, cb)
  else if('object' === typeof obj)
    return hash(obj, cb)
  else
    return list([].slice.call(arguments))
}

I could imagine that Promise.all is another 2 state machine with if(isIterable()) { currentImplementation() } else { propertiesImplementation() }

My other use case for this is to have say a hash of teachers and wanting to fetch them asynchronously.

If I fetch them asynchronously as an array I would still do

Promise.all(...).then(function (list) {
  var teacherHash = list.reduce(function (acc, value) {
    acc[value.name] = value
    return acc
  }, {})
})

Having Promise.all support an object would remove this conversion step.

Raynos commented 10 years ago

@domenic note that before I used continuable-para I authored and used continuable-list & continuable-hash as explicit and independent flow control primitives. From personal experience it's tedious to use & import both when the alternative is just using a single parallel function that works on the common structures.

Anything more complex is obvouisly out of scope and should not be in the core Promise definition

petkaantonov commented 10 years ago

Well, in promises_unwrapping it actually takes an iterable.


However, assuming the usual Promise.all that works on Arrays.

I am not convinced by the arguments in 374 either. The obvious thing to do is to check for a non-primitive and only use enumerable own properties as returned by Object.keys() (as implied in the last comment in that issue but ignored). Arrays (Array.isArray) and Promises (Promise.isPromise) are special cased and arrays with custom properties are not considered for those custom properties.

If you have reasonable code that needs to pass an Error object as an argument to Promise.all(), I would like to see it. Even if you had, it would be treated like any other object.

If the arguments mentioned in 374 are the only ones, I will probably implement this in my library.

domenic commented 10 years ago

In general overloading and return-type polymorphism is discouraged. If this becomes a popular user-space extension we could consider Promise.allObject or similar. But it is not in the scope of the September 2013 TC39 consensus, so closing for now.