Creating a (ES6) promise without starting to resolve it
Good question!
The resolver passed to the promise constructor intentionally runs synchronous in order to support this use case:
var deferreds = [];
var p = new Promise(function(resolve, reject){
deferreds.push({resolve: resolve, reject: reject});
});
Then, at some later point in time:
deferreds[0].resolve("Hello"); // resolve the promise with "Hello"
The reason the promise constructor is given is that:
- Typically (but not always) resolution logic is bound to the creation.
- The promise constructor is throw safe and converts exceptions to rejections.
Sometimes it doesn't fit and for that it the resolver runs synchronously. Here is related reading on the topic.
I want to add my 2 cents here. Considering exactly the question "Creating a es6 Promise without starting resolve it" I solved it creating a wrapper function and calling the wrapper function instead. Code:
Let's say we have a function f
which returns a Promise
/** @return Promise<any> */
function f(args) {
return new Promise(....)
}
// calling f()
f('hello', 42).then((response) => { ... })
Now, I want to prepare a call to f('hello', 42)
without actually solving it:
const task = () => f('hello', 42) // not calling it actually
// later
task().then((response) => { ... })
Hope this will help someone :)
Referencing Promise.all()
as asked in the comments (and answered by @Joe Frambach), if I want to prepare a call to f1('super')
& f2('rainbow')
, 2 functions that return promises
const f1 = args => new Promise( ... )
const f2 = args => new Promise( ... )
const tasks = [
() => f1('super'),
() => f2('rainbow')
]
// later
Promise.all(tasks.map(t => t()))
.then(resolvedValues => { ... })
How about a more comprehensive approach?
You could write a Constructor that returns a new Promise decorated with .resolve()
and .reject()
methods.
You would probably choose to name the constructor Deferred
- a term with a lot of precedence in [the history of] javascript promises.
function Deferred(fn) {
fn = fn || function(){};
var resolve_, reject_;
var promise = new Promise(function(resolve, reject) {
resolve_ = resolve;
reject_ = reject;
fn(resolve, reject);
});
promise.resolve = function(val) {
(val === undefined) ? resolve_() : resolve_(val);
return promise;//for chainability
}
promise.reject = function(reason) {
(reason === undefined) ? reject_() : reject_(reason);
return promise;//for chainability
}
promise.promise = function() {
return promise.then(); //to derive an undecorated promise (expensive but simple).
}
return promise;
}
By returning a decorated promsie rather than a plain object, all the promise's natural methods/properties remain available in addition to the decorations.
Also, by handling fn
, the revealer pattern remains availble, should you need/choose to use it on a Deferred.
DEMO
Now, with the Deferred()
utility in place, your code is virtually identical to the jQuery example.
var deferreds = {};
function waitFor(key: string): Promise<any> {
if (key in promises) {
return deferreds[key].promise();
}
var def = Deferred();
deferreds[key] = def;
return def.promise();
}
Things are slowly getting better in JavaScript land, but this is one case where things are still unnecessarily complicated. Here's a simple helper to expose the resolve and reject functions:
Promise.unwrapped = () => {
let resolve, reject, promise = new Promise((_resolve, _reject) => {
resolve = _resolve, reject = _reject
})
promise.resolve = resolve, promise.reject = reject
return promise
}
// a contrived example
let p = Promise.unwrapped()
p.then(v => alert(v))
p.resolve('test')
Apparently there used to be a Promise.defer
helper, but even that insisted on the deferred object being separate from the promise itself...