I created this little helper to expose resolve
and reject
outside of the Promise's constructor
export function createPromise() {
let resolve,reject;
let promise = new Promise((r,j) => {
resolve = r;
reject = j;
});
Object.assign(promise,{resolve,reject});
return promise;
}
Because sometimes it's just really awkward to wrap your entire script in
new Promise((resolve,reject) => {
// hundreds of lines of code, most which have nothing
// to do with this Promise but I need the Promise object
// at the top level somewhere so that I can expose it,
// but it won't be resolved until some deeply nested construct is hit
})
Or to give a more concrete example:
let p1 = kb.createPromise();
let p2 = kb.createPromise();
Promise.all([p1,p2]).then(() => {
$('#calendar-bookings').scrollIntoView({
duration: 200,
direction: 'y'
});
});
$('#bookings-table')
.dataTable(getOptions(dtSourceUrl, {date, driverOptions}))
.one('draw.dt', () => p1.resolve());
ReactDOM.render(
<VehicleTimeline
start={dateMom.toDate()}
onLoad={() => p2.resolve()} />,
document.getElementById('vehicle-timeline')
);
This way I also don't have to worry about whether [I stand corrected, the resolve()
is called synchronously before I even get a chance to bind my .then
..then
will fire immediately] I think this is pretty clear: create two promises, bind the .then
, and only after is it even conceivable that they're resolved.
Sure, this would allow anyone who you've passed your Promise off to to resolve it (i.e. giving control away from the Promise creator and to the consumer) but if the consumer fires it early, that's their loss because presumably they're the ones interested in the event, no?
Also, this would give the consumer the ability to reject()
the Promise which they could abuse as a sort of Promise cancellation. I'm not saying that's a good idea, but I don't think the extra freedom is necessarily bad either.
Is there anything else I'm missing? Any problems with my createPromise
method?
This is a variation of the deferred
pattern except that you return the promise object with the resolve/reject functions in the promise object. The original deferred
pattern created an object with the promise and the resolve/reject functions separately. So you can pass out the promise without exposing the controls.
To be honest, there are very little places where actually breaking out of the constructor scope is needed. In my opinion it's a little bit easier to make a mistake with this pattern and end up with unresolved promises than with the constructor pattern. As far as I can tell really, the only benefit from the constructor pattern over the deferred pattern that you get is that you can throw and reject the promise immediately (through a sync mistake).