Just for fun/learning I wanted to extend Promise and found, via incredibly knowledgable people on stackoverflow, that it can't be done with the standard old syntax. But I wanted to anyway, so I thought of creating my own IPromise class that composes a Promise object and allows others to inherit from it using the old non ES6 syntax.
I want to know how this implementation differs from a universe where the builtin Promise is directly inheritable, and if anyone has insight, why the implementors of the builtin Promise didn't allow inheritance with the old syntax.
Here's my extendible/inheritable IPromise class:
// Inheritable Promise
IPromise = function(executor) {
this.promise = new Promise(executor);
};
IPromise.prototype = Object.create(Promise.prototype);
IPromise.prototype.constructor = IPromise;
IPromise.prototype.catch = function(fail) {
return this.promise.catch(fail);
}
IPromise.prototype.then = function(success, fail) {
return this.promise.then(success, fail);
};
// Usage
// No different to builtin Promsise
(new IPromise(function(resolve, reject) { return resolve(true); }))
.then(function(response) {
console.log('IPromise then is like Promise then', response);
})
.catch(function(error) {
console.log('IPromise catch is like Promise catch', error);
});
Here's an example of extending it for a batch ajax that waits for all requests to complete regardless of whether any of them fail. Slightly different to builtin functionality.
// Inheriting
// You can inherit from IPromise like you would any normal class.
BatchAjax = function(queries) {
var batchAjax = this;
this.queries = queries;
this.responses = [];
this.errorCount = 0;
IPromise.call(this, function(resolve, reject) {
batchAjax.executor(resolve, reject);
});
};
BatchAjax.prototype = Object.create(IPromise.prototype);
BatchAjax.prototype.constructor = BatchAjax;
BatchAjax.prototype.executor = function(resolve, reject) {
var batchAjax = this;
$.each(this.queries, function(index) {
var query = this;
query.success = function (result) {
batchAjax.processResult(result, index, resolve, reject);
};
query.error = function (jqXhr, textStatus, errorThrown) {
batchAjax.errorCount++;
var result = {
jqXhr: jqXhr, textStatus: textStatus, errorThrown: errorThrown
};
batchAjax.processResult(result, index, resolve, reject);
};
$.ajax(query);
});
};
BatchAjax.prototype.processResult = function(result, index, resolve, reject) {
this.responses[index] = result;
if (this.responses.length === this.queries.length) {
if (this.errorCount === 0) {
resolve(this.responses);
} else {
reject(this.responses);
}
}
};
// Usage
// Inheriting from IPromise is boring, which is good.
var baseUrl = 'https://jsonplaceholder.typicode.com';
(new BatchAjax([{url: baseUrl + '/todos/4'}, {url: baseUrl + '/todos/5'}]))
.then(function(response) {console.log('Yay! ', response);})
.catch(function(error) {console.log('Aww! ', error);});
Bear in mind that I'm just learning and this isn't meant to be useful, just interesting. But feel free to give brutal criticism, I'm open to the idea that this is a stupid thing to do : )
Cheers for taking a look and sorry for all the code!
edit: Here's the question where I was originally trying to extend the promise: Extending a Promise in javascript. It doesn't seem to work with the old syntax because the Promise constructor throws a Type error if it notices that it's initialising something that's not a Promise (I think).
edit2: jfriend00's comment highlighted something interesting. What should IPromise.then return? At the moment it's just a Promise, but should it be an IPromise?
IPromise.prototype.then = function(success, fail) {
this.promise.then(success, fail);
return new IPromise(whatGoesHere?);
};
Or an instance of whatever class has inherited from it.
IPromise.prototype.then = function(success, fail) {
this.promise.then(success, fail);
return new this.constructor(whatGoesHere?);
};
Or could it just return this?
IPromise.prototype.then = function(success, fail) {
this.promise.then(success, fail);
return this;
};
I know Promise.then returns a Promise, but I don't know how that Promise is setup or expected to behave. That's why I avoided the issue and just returned the Promise from this.promise.then. Are there any other sensible solutions?