Promise.defer standard? - javascript

I was working with Promises and prefer to use it like this:
function Deferred() {
this.resolve = null;
this.reject = null;
this.promise = new Promise(function(resolve, reject) {
this.resolve = resolve;
this.reject = reject;
}.bind(this));
Object.freeze(this);
}
function somethingAsync() {
var deferred = new Deferred();
// do stuff then deferred.resolve();
return deferred.promise;
}
I just came across though in Firefox Promise.defer() which gives me the same thing, is this standard? Or just specific to Firefox? I can't find it in the Promise docs of Firefox even - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Promise.defer was a suggestion at one point but it was decided to not include it in the spec but to instead include the promise constructor which uses the revealing constructor pattern.
It was implemented in Firefox and Chrome and later removed from Chrome. It is not a standard but was a proposal at one point.
Your usage of the promise constructor was explicitly supported as a use case when it was designed.
The reason the committee decided to go with the promise constructor was because it guards against synchronous throws by default:
new Promise((resolve, reject) => {
thisThrowsSynchronously();
});
Had the promise constructor not done this - you would have to potentially .catch and } catch(e) { on every promise returning function invocation which can be frustrating. The promise constructor establishes an invariant where .catch is sufficient.
I'd also like to point out that outside of converting callback APIs - I can count the number of times I've used the promise constructor on one hand. Typically your code should have close to zero deferreds or usages of the promise constructor.

Related

Extending Promise results in constructor called twice, how come? [duplicate]

I want to extend native Javascript Promise class with ES6 syntax, and be able to call some asynchronous function inside the subclass constructor. Based on async function result the promise must be either rejected or resolved.
However, two strange things happen when then function is called:
subclass constructor is executed twice
"Uncaught TypeError: Promise resolve or reject function is not callable" error is thrown
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
The reasoning is simple but not necessarily self evident.
.then() returns a promise
if then is called on a subclass of Promise, the returned promise is an instance of the subclass, not Promise itself.
the then returned promise is constructed by calling the subclass constructor, and passing it an internal executor function that records the value of resolve and reject arguments passed to it for later use.
"later use" covers resolving or rejecting the promise returned by then asynchronously when monitoring execution of onfulfilled or onrejected handlers (later) to see if they return a value (which resolves the then returned promise) or throw an error (which rejects the promise).
In short then calls internally obtain and record references to the resolve and reject functions of promises they return.
So regarding the question,
new MyPromise( 'p1')
works fine and is the first call to the subclass constructor.
.then( someFunction)
records someFunction in a list of then calls made on new MyPromise (recall then can be called multiple times) and attempts to create a return promise by calling
new MyPromise( (resolve, reject) => ... /* store resolve reject references */
This is the second call to the subclass constructor coming from then code. The constructor is expected to (and does) return synchronously.
On return from creating the promise to return, the .then method makes an integrity check to see if the resolve and reject functions it needs for later use are in fact functions. They should have been stored (in a list) along with callbacks provided in the then call.
In the case of MyPromise they are not. The executor passed by then, to MyPromise, is not even called. So then method code throws a type error "Promise resolve or reject function is not callable" - it has no means of resolving or rejecting the promise it is supposed to return.
When creating a subclass of Promise, the subclass constructor must take an executor function as its first argument, and call the executor with real resolve and reject functional arguments. This is internally required by then method code.
Doing something intricate with MyPromise, perhaps checking the first parameter to see if it is a function and calling it as an executor if it is, may be feasible but is outside the scope of this answer! For the code shown, writing a factory/library function may be simpler:
function namedDelay(name, delay=1000, value=1) {
var promise = new Promise( (resolve,reject) => {
setTimeout(() => {
resolve(value)
}, delay)
}
);
promise.name = name;
return promise;
}
namedDelay( 'p1')
.then(result => {
console.log('fulfilled, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
;TLDR
The class extension to Promise is not an extension. If it were it would need to implement the Promise interface and take an executor function as first parameter. You could use a factory function to return a Promise which is resolved asynchronously (as above), or hack the posted code with
MyPromise.prototype.constructor = Promise
which causes .then to return a regular Promise object. The hack itself refutes the idea that a class extension is taking place.
Promise Extension Example
The following example shows a basic Promise extension that adds properties supplied to the constructor. Of note:
The Symbol.toString getter only affects the output of converting an instance to a string. It does not change "Promise" to "MyPromise" when logging an instance object on browser consoles tested.
Firefox 89 (Proton) is not reporting own properties of extended instances while Chrome does - the reason test code below logs instance properties by name.
class MyPromise extends Promise {
constructor(exec, props) {
if( typeof exec != "function") {
throw TypeError( "new MyPromise(executor, props): an executor function is required");
}
super((resolve, reject) => exec(resolve,reject));
if( props) {
Object.assign( this, props);
}
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
// Test the extension:
const p1 = new MyPromise( (resolve, reject) =>
resolve(42),
{id: "p1", bark: ()=>console.log("woof") });
console.log( "p1 is a %s object", p1.constructor.name);
console.log( "p1.toString() = %s", p1.toString());
console.log( "p1.id = '%s'", p1.id);
console.log( "p1 says:"); p1.bark();
const pThen = p1.then(data=>data);
console.log( "p1.then() returns a %s object", pThen.constructor.name);
let pAll = MyPromise.all([Promise.resolve(39)]);
console.log( "MyPromise.all returns a %s object", pAll.constructor.name);
try { new MyPromise(); }
catch(err) {
console.log( "new MyPromise() threw: '%s'", err.message);
}
The best way I found to extend a promise is
class MyPromise extends Promise {
constructor(name) {
// needed for MyPromise.race/all ecc
if(name instanceof Function){
return super(name)
}
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
// you can also use Symbol.species in order to
// return a Promise for then/catch/finally
static get [Symbol.species]() {
return Promise;
}
// Promise overrides his Symbol.toStringTag
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
new MyPromise('p1')
.then(result => {
console.log('resolved, result: ', result)
})
.catch(err => {
console.error('err: ', err)
})
The post by asdru contains the correct answer, but also contains an approach (constructor hack) that should be discouraged.
The constructor hack checks if the constructor argument is a function. This is not the way to go as the ECMAScript design contains a specific mechanism for sub-classing Promises via Symbol.species.
asdru's comment on using Symbol.species is correct. See the explanation in the current ECMAScript specification:
Promise prototype methods normally use their this value's constructor
to create a derived object. However, a subclass constructor may
over-ride that default behaviour by redefining its ##species property.
The specification (indirectly) refers to this note in the sections on finally and then (look for mentions of SpeciesConstructor).
By returning Promise as the species constructor, the problems that traktor's answer analyzes so clearly are avoided. then calls the Promise constructor, but not the sub-classed MyPromise constructor. The MyPromise constructor is called only once with the name argument and no further argument-checking logic is needed or appropriate.
Therefore, the code should simply be:
class MyPromise extends Promise {
constructor(name) {
super((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
this.name = name
}
static get [Symbol.species]() {
return Promise;
}
get [Symbol.toStringTag]() {
return 'MyPromise';
}
}
Less is more!
Some notes:
MDN has an example for the use of the species symbol in extending Array.
The most recent browser versions (Chrome, FF, Safari, Edge on MAC and Linux) handle this correctly, but I have no information on other browsers or legacy versions.
Symbol.toStringTag is a very nice touch, but not required. Most browsers use the value returned for this symbol to identify the sub-classed promise in the console, but, beware, FF does not - this could easily be confusing. In all browsers, however, new MyPromise('mine').toString() yields "[object MyPromise]".
All of this is also unproblematic if you author in Typescript.
As noseratio points out, a primary use-case for extending Promises is the wrapping of (legacy) APIs that support abort or cancel logic (FileReader, fetch, ...).
You have to make it thenable by implementing the then method.
Otherwise, that of the superclass, Promise, will be called, and it’ll try to create another Promise with your MyPromise’ constructor, which is not compatible with the original Promise constructor.
The thing is, it’s tricky to properly implement the then method that works just like Promise’s does. You’ll likely end up having an instance of Promise as a member, not as a superclass.
Summarizing the above into a short tl;dr for those who just want to get something working and move on, you just need to add this to your class body:
static get [Symbol.species]() {
return Promise;
}
A full TypeScript example:
class MyPromise extends Promise<string> {
constructor(name: string) {
super((resolve) => {
setTimeout(() => {
resolve(name)
}, 1000)
})
this.name = name;
}
static get [Symbol.species]() {
return Promise;
}
}
const p = new MyPromise('hi');
p.name === 'hi'
const x: string = await p;
x === 'hi'
That's all you need.
Forgoing Symbol.toStringTag may make MyPromise just look like a Promise in some debugging contexts, and you won't be able to do MyPromise.race or MyPromise.all, but that's fine: you can use Promise.all with a list of MyPromises.

JS promise revealing constructor in YDKJS

I'm reading the Async & Performance book of the YDKJS series and am having trouble wrapping my head around the revealing constructor pattern.
Here's the example the book gives:
function foo(x) {
// start doing something that could take a while
// construct and return a promise
return new Promise( function(resolve,reject){
// eventually, call `resolve(..)` or `reject(..)`,
// which are the resolution callbacks for
// the promise.
} );
}
var p = foo( 42 );
function bar(fooPromise) {
// listen for `foo(..)` to complete
fooPromise.then(
function(){
// `foo(..)` has now finished, so
// do `bar(..)`'s task
},
function(){
// oops, something went wrong in `foo(..)`
}
);
}
bar( p );
I have two questions:
I think the intention is not to expose the resolve and reject logic to the outside, which creates a good separation of concern between the promise logic and what happens next with .then() in the function (in this case bar()) that "listens" to the Promise. Am I on track here?
The example foo() confuses me. In the comment // start doing something that could take a while, what exactly is this? Is this where your async call would be? Shouldn't it be inside the function in the return statement? I don't understand how this would work otherwise.
I think the intention is not to expose the resolve and reject logic to the outside, which creates a good separation of concern
Yes, that and the difficulty of catching exceptions in the deferred pattern, an alternative way to expose the resolvers only to the function that creates the task but not those that consume the result.
The example foo() confuses me. In the comment // start doing something that could take a while, what exactly is this? Is this where your async call would be?
Yes, that's what they meant.
Shouldn't it be inside the function in the return statement?
Yes, indeed it should be. A specific example might be
function delay(x) {
return new Promise(function(resolve) { // construct and return a promise
setTimeout(function() { // start doing something that could take a while
resolve(); // eventually, call one of the resolution callbacks
}, x);
});
}
It's of course possible that starting the task and waiting for its result are two separate statements, so they could in theory be put apart:
function ajax(x) {
var xhr = new XMLHttpRequest();
xhr.open("GET", x);
xhr.send(); // start doing something that could take a while
return new Promise(function(resolve, reject) { // construct and return a promise
xhr.onload = resolve;
xhr.onerror = reject; // eventually, call one of the resolution callbacks
});
}
but that is a bad practise, as when new, open or send throw you would not get a rejected promise back but an exception. So everything from var xhr = … should be inside the promise constructor callback.
For 2., in general and without further details, yes, you are correct; the resolver function passed to Promise constructor executes immediately. One exception would be use of async/await, though async/await does not appear at JavaScript at Question.

Why does the Promise constructor need an executor?

When using Promises, why can't triggers for resolve and reject be defined elsewhere in the codebase?
I don't understand why resolve and reject logic should be localized where the promise is declared. Is this an oversight, or is there a benefit to mandating the executor parameter?
I believe the executor function should be optional, and that its existence should determine whether the promise encapsulates resolution or not. The promise would be much more extensible without such mandates, since you don't have to initiate async right away. The promise should also be resettable. It's a 1 shot switch, 1 or 0, resolve() or reject(). There are a multitude of parallel and sequential outcomes that can be attached: promise.then(parallel1) and promise.then(parallel2) and also promise.then(seq1).then(seq2) but reference-privileged players cannot resolve/reject INTO the switch
You can construct a tree of outcomes at a later time, but you can't alter them, nor can you alter the roots (input triggers)
Honestly, the tree of sequential outcomes should be edittable as well.. say you want to splice out one step and do something else instead, after you've declared many promise chains. It doesn't make sense to reconstruct the promise and every sequential function, especially since you can't even reject or destroy the promise either...
This is called the revealing constructor pattern coined by Domenic.
Basically, the idea is to give you access to parts of an object while that object is not fully constructed yet. Quoting Domenic:
I call this the revealing constructor pattern because the Promise constructor is revealing its internal capabilities, but only to the code that constructs the promise in question. The ability to resolve or reject the promise is only revealed to the constructing code, and is crucially not revealed to anyone using the promise. So if we hand off p to another consumer, say
The past
Initially, promises worked with deferred objects, this is true in the Twisted promises JavaScript promises originated in. This is still true (but often deprecated) in older implementations like Angular's $q, Q, jQuery and old versions of bluebird.
The API went something like:
var d = Deferred();
d.resolve();
d.reject();
d.promise; // the actual promise
It worked, but it had a problem. Deferreds and the promise constructor are typically used for converting non-promise APIs to promises. There is a "famous" problem in JavaScript called Zalgo - basically, it means that an API must be synchronous or asynchronous but never both at once.
The thing is - with deferreds it's possible to do something like:
function request(param) {
var d = Deferred();
var options = JSON.parse(param);
d.ajax(function(err, value) {
if(err) d.reject(err);
else d.resolve(value);
});
}
There is a hidden subtle bug here - if param is not a valid JSON this function throws synchronously, meaning that I have to wrap every promise returning function in both a } catch (e) { and a .catch(e => to catch all errors.
The promise constructor catches such exceptions and converts them to rejections which means you never have to worry about synchronous exceptions vs asynchronous ones with promises. (It guards you on the other side by always executing then callbacks "in the next tick").
In addition, it also required an extra type every developer has to learn about where the promise constructor does not which is pretty nice.
FYI, if you're dying to use the deferred interface rather than the Promise executor interface despite all the good reasons against the deferred interface, you can code one trivially once and then use it everywhere (personally I think it's a bad idea to code this way, but your volume of questions on this topic suggests you think differently, so here it is):
function Deferred() {
var self = this;
var p = this.promise = new Promise(function(resolve, reject) {
self.resolve = resolve;
self.reject = reject;
});
this.then = p.then.bind(p);
this.catch = p.catch.bind(p);
if (p.finally) {
this.finally = p.finally.bind(p);
}
}
Now, you can use the interface you seem to be asking for:
var d = new Deferred();
d.resolve();
d.reject();
d.promise; // the actual promise
d.then(...) // can use .then() on either the Deferred or the Promise
d.promise.then(...)
Here a slightly more compact ES6 version:
function Deferred() {
const p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = p.then.bind(p);
this.catch = p.catch.bind(p);
if (p.finally) {
this.finally = p.finally.bind(p);
}
}
Or, you can do what you asked for in your question using this Deferred() constructor:
var request = new Deferred();
request.resolve();
request.then(handleSuccess, handleError);
But, it has the downsides pointed out by Benjamin and is not considered the best way to code promises.

How to create a fulfilled promise in mpromise

I have a node#4.3.1 + mongo + mongoose#4.4.4 project which I think is providing me with the mpromise library (the mongoose docs imply this)
Assuming it is mpromise, I'm stumped by the very simple job of creating an already fulfilled promise (so I can stub a function that should return a promise).
The mpromise doc (see the "Chain" section) says I can do this:
function makeMeAPromise(i) {
var p = new Promise;
p.fulfill(i);
return p;
}
But this fails with the exception "Promise resolver undefined is not a function".
Am I using mpromise? Is the doc lying? How do I create a resolved promise?
edit: This works
return new Promise(function(fulfill, reject) { fulfill("really? this can't be the only way"); });
but that can't be the simplest way, right?
Mongo doesn't override the default Promise object in Node as far as I know. So the default ways should work just fine:
const resolvedPromise1 = new Promise(f => f("Fullfilled!"));
const resolvedPromise2 = Promise.resolve("Fullfilled!");

Extend ES6 Promise to convert callback to Deferred pattern

I would like to know what do you think about this kind of extension for ES6 Promise (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise):
Promise.create = function() {
var deferred;
var promise = new Promise(function (resolve, reject) {
deferred = {
resolve: resolve,
reject: reject
};
});
promise.deferred = deferred;
return promise;
}
Like that it avoid using the first callback to get cleaner code:
var requestsdeferred = Promise.create();
obj.myFunctionWithCallback(function(){
obj.mySecondFunctionWithCallback(function(){
requestsdeferred.resolve('all done!');
});
});
requestsdeferred.then(function(result) {
});
instead of:
var p = new Promise(function(resolve, reject){
obj.myFunctionWithCallback(function(){
obj.mySecondFunctionWithCallback(function(){
resolve('all done!');
});
});
});
p.then(function(){
});
Which need a callback.
What do you think ?
Neither is the correct/regular usage of a promise. The usual way to use a promise would be:
ajax.get('/get').then(function(){
return ajax.get('/cart');
}).then(function(){
alert('all done!');
});
Promises chain, you can return a promise from a then handler and it will cause the returned promise from the then to wait for its completion and assume its state.
Of course, unless the promises depend you can (and likely should) execute them concurrently:
Promise.all([ajax.get("/get"), ajax.get("/cart")]).then(function(results){
// both done here, concurrently
});
Avoid the explicit construction anti-pattern, no need to create a deferred. The reason the API does not look the way you describe it is so that if you throw synchronously it will get converted to a rejection and you won't have to add both a .catch and a catch (e){ handler to your code which is error prone.
It's just an obfuscation of the ES6 standard, and I don't see much benefit to your addition. It's just a layer of abstraction that isn't really needed.
I'd suggest a different approach that plays nice with ES6 promises...
The following is correct, but irrelevant (see comments) :
Any "thenable" can be turned into a standard Promise with Promise.resolve, so if your ajax is (for instance) created with jQuery's $.ajax, you might:
var prom1 = Promise.resolve(ajax.get('/get'));
var prom2 = Promise.resolve(ajax.get('/cart'));
We can create these together (so the requests run concurrently), then wait for all of the promises to complete with Promise.all:
Promise.all([req1, req2])
.then(function(vals){
//and get the results in the callback
var getData = vals[0];
var cartData = vals[1];
});

Categories