Extending a Promise in javascript - javascript

I'm learning about classes and inheritance in javascript. I thought that the following is a fairly standard way of extending an existing object as I got the style from the MDN docs on Object.create
I was expecting to see 'ok' and then 'Yay! Hello' in the console, but instead I go this error:
Uncaught TypeError: #<MyPromise> is not a promise
at new MyPromise (<anonymous>:5:17)
at <anonymous>:19:6
It looks like the Promise constructor is throwing an exception because it can tell that the object I've given it to initialise isn't a straightforward Promise.
I want the Promise constructor to initialise my object as if it was a Promise object, so I can then extend the class. Why wouldn't they write the Promise constructor to work with this common pattern? Am I doing something wrong? Cheers for taking a look!
MyPromise = function(message, ok) {
var myPromise = this;
this.message = message;
this.ok = ok;
Promise.call(this, function(resolve, reject) {
if(this.ok) {
console.log('ok');
resolve(myPromise.message);
} else {
console.log('not ok');
reject(myPromise.message);
}
});
};
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
(new MyPromise('Hello', true))
.then(function(response) {console.log('Yay! ' + response);})
.except(function(error) {console.log('Aww! ' + error);});
I was originally trying to make a BatchAjax class that you could use like:
(new BatchAjax([query1, query2]))
.then(function(response) {console.log('Fires when all queries are complete.');});
It was just a bit of fun really.

The native Promise class (like Error and Array) cannot be correctly subclassed with the old ES5-style mechanism for subclassing.
The correct way to subclass Promise is through class syntax:
class MyPromise extends Promise {
}
Example:
class MyPromise extends Promise {
myMethod() {
return this.then(str => str.toUpperCase());
}
}
// Usage example 1
MyPromise.resolve("it works")
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
// Usage example 2
new MyPromise((resolve, reject) => {
if (Math.random() < 0.5) {
resolve("it works");
} else {
reject(new Error("promise rejected; it does this half the time just to show that part working"));
}
})
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
If it's your goal to do that without class, using mostly ES5-level features, you can via Reflect.construct. Note that Reflect.construct is an ES2015 feature, like class, but you seem to prefer the ES5 style of creating classes.
Here's how you do that:
// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
return Reflect.construct(Promise, [executor], MyPromise);
};
// Make `MyPromise` inherit statics from `Promise`
Object.setPrototypeOf(MyPromise, Promise);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
return this.then(str => str.toUpperCase());
};
Then use it just like Promise:
MyPromise.resolve("it works")
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
or
new MyPromise(resolve => resolve("it works"))
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
etc.
Live Example:
// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
return Reflect.construct(Promise, [executor], MyPromise);
};
// Make `MyPromise` inherit statics from `Promise`
Object.setPrototypeOf(MyPromise, Promise);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
return this.then(str => str.toUpperCase());
};
// Usage example 1
MyPromise.resolve("it works")
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
// Usage example 2
new MyPromise((resolve, reject) => {
if (Math.random() < 0.5) {
resolve("it works");
} else {
reject(new Error("promise rejected; it does this half the time just to show that part working"));
}
})
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
If you want to avoid changing the prototype of MyPromise, you can copy the static properties over, but it's not quite the same thing:
// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
return Reflect.construct(Promise, [executor], MyPromise);
};
// Assign the statics (`resolve`, `reject`, etc.) to the new constructor
Object.assign(
MyPromise,
Object.fromEntries(
Reflect.ownKeys(Promise)
.filter(key => key !== "length" && key !== "name")
.map(key => [key, Promise[key]])
)
);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
return this.then(str => str.toUpperCase());
};
Using it is the same, of course.
Live Example:
// Create a constructor that uses `Promise` as its super and does the `super` call
// via `Reflect.construct`
const MyPromise = function(executor) {
return Reflect.construct(Promise, [executor], MyPromise);
};
// Assign the statics (`resolve`, `reject`, etc.) to the new constructor
Object.assign(
MyPromise,
Object.fromEntries(
Reflect.ownKeys(Promise)
.filter(key => key !== "length" && key !== "name")
.map(key => [key, Promise[key]])
)
);
// Create the prototype, add methods to it
MyPromise.prototype = Object.create(Promise.prototype);
MyPromise.prototype.constructor = MyPromise;
MyPromise.prototype.myMethod = function() {
return this.then(str => str.toUpperCase());
};
// Usage example 1
MyPromise.resolve("it works")
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));
// Usage example 2
new MyPromise((resolve, reject) => {
if (Math.random() < 0.5) {
resolve("it works");
} else {
reject(new Error("promise rejected; it does this half the time just to show that part working"));
}
})
.myMethod()
.then(result => console.log(result))
.catch(error => console.error(error));

My latest solution is to compose a Promise object into my class as this.promise and then pretend to be inheriting from Promise by overriding all the instance methods of Promise and passing them on to the this.promise object. Hilarity ensues. I'd really welcome people pointing out the drawbacks to this approach.
Nothing is too obvious for me to have missed.
When I paste this code into the Chrome console, it seems to work. That's as far as I comprehend.
Cheers for taking a look.
BatchAjax = function(queries) {
var batchAjax = this;
this.queries = queries;
this.responses = [];
this.errorCount = 0;
this.promise = new Promise(function(resolve, reject) {
batchAjax.executor(resolve, reject);
});
};
BatchAjax.prototype = Object.create(Promise.prototype);
BatchAjax.prototype.constructor = BatchAjax;
BatchAjax.prototype.catch = function(fail) {
return this.promise.catch(fail);
}
BatchAjax.prototype.then = function(success, fail) {
return this.promise.then(success, fail);
};
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
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);});

As #tobuslieven's answer suggested, it's not necessary to extend Promise. Below is a simplified sample.
ES6:
class MyPromise {
async executor () {
if(!this.name) {
throw new Error('whoops!')
}
console.log('Hello', this.name)
}
then () {
const promise = this.executor()
return promise.then.apply(promise, arguments)
}
catch () {
const promise = this.executor()
return promise.catch.apply(promise, arguments)
}
}
ES5:
function MyPromise () { }
MyPromise.prototype.executor = function () {
var self = this
return new Promise(function (resolve, reject) {
if (!self.name) {
return reject(new Error('whoops!'))
}
console.log('Hello', self.name)
resolve()
})
}
MyPromise.prototype.then = function () {
var promise = this.executor()
return promise.then.apply(promise, arguments)
}
MyPromise.prototype.catch = function () {
var promise = this.executor()
return promise.catch.apply(promise, arguments)
}
both can be tested by:
var promise = new MyPromise()
promise.name = 'stackoverflow'
promise
.then(function () {
console.log('done!')
})
.catch(console.log)

Related

ES6 Class returning Promise/having a .then() method?

I would like to be able to .then() a instantiated object while following the Promise standards.
Or is this not recommended?
I tried the following but I don't think it's the right approach...
class MyClass extends Promise {
constructor(){
this.loaded = false;
//Non promise third-party callback async function
someAsyncFunction( result => {
this.loaded = true;
this.resolve(result);
}
}
}
const myClass = new MyClass();
myClass.then( result => {
console.log(result);
console.log(myClass.loaded);
// >>true
})
Edit:
What I ended up doing was the following, but I'm not sure about using .load().then()
class MyClass {
constructor(){
this.loaded = false;
}
load(){
return new Promise( resolve => {
//Non promise third-party callback async function
someAsyncFunction( result => {
this.loaded = true;
resolve(result);
}
})
}
}
const myClass = new MyClass();
myClass.load().then( result => {
console.log(result);
console.log(myClass.loaded);
// >>true
})
You can have custom then-able objects, but your intent is not quite clear. If the code should ensure that the instance of MyClass is ready before you use it, then you should use either a factory function returning that object as soon as it is ready, or if certain functions depend on async loading make those functions async too.
The then-able object does not prevent you from using before it was resolved, so that design does not help you with maintainability or error safety.
Factory function:
function createMyClass(options) {
const myClass = new MyClass();
return loadData(options).then( (result) => {
myClass.loaded = true;
myClass.result = result;
return myClass;
})
}
createMyClass({/*some options*/}).then( myClass => {
console.log(myClass.result);
console.log(myClass.loaded);
})
Load the result on demand:
class MyClass {
constructor(options) {
this.loaded = false;
this.options = options;
}
result() {
// only request the data if it was not already requested
if (!this._result) {
this._result = loadData(this.options).then(result => {
this.loaded = true
return result
});
}
return this._result
}
}
var myClass = new MyClass({/*....*/})
myClass.result().then(result => {
console.log(result)
})
// could be called another time, and the data is not requested over again,
// as the Promise is reused
myClass.result().then(result => {
console.log(result)
})
Or is this not recommended?
Not only is this not recommended but it will also never work.
You should only use the constructor to define initial state values or
perform construction value validations etc.
You can use an init() method to do what you want like so:
class MyClass {
constructor(){
this.loaded = false
}
init() {
return someAsyncFunction()
.then(value => {
this.loaded = true
return value
})
}
}
This is how you can write the promise
const someAsyncFunction = (parameters) => {
return new Promise((resolve, reject) => {
if (success) {
resolve();
} else {
reject();
}
});
};
someAsyncFunction
.then((result) => {
})
.catch((err) => {
});

How to stub a class which returns a class?

I have a method which i'm unit testing and i'm trying to test both
...
let db = new MockDb()
...
db.query(query)
.then(cursor => {
return cursor.next()
.then(result => {
if (result !== undefined || !data.id) { <-- WANT TO STUB CURSOR.NEXT() SO I CAN TEST RESULT BEING DIFFERENT VALUES
data.id = uuidv4()
}
...
I have this file that is used for mocking/stubbing which contains 2 classes, but only exposes one of them
class MockDb {
query (aql) {
return new Promise((resolve, reject) => {
return resolve(new MockCursor())
})
}
}
class MockCursor {
next () {
return new Promise((resolve, reject) => {
return resolve({foo: 'bar'})
})
}
}
module.exports = MockDb
How can I stub what cursor.next() returns in this case?
Does NOT work - gives error TypeError: cursor.stub is not a function
before(() => {
let cursor = sinon.stub()
cursor.stub('next').return(undefined)
sinon.stub(db, 'query').resolves(cursor)
})
If i expose the MockCursor class I can simply do this:
let db = new MockDb()
let cursor = new MockCursor()
sinon.stub(cursor, 'next').resolves(undefined)
sinon.stub(db, 'query').resolves(cursor)
But i was just trying to do it without having to expose the MockCursor class explicitly
This should work:
const cursor = sinon
.stub()
.returns({
next: () => Promise.resolve(undefined)
});
sinon.stub(db, 'query').resolves(cursor);

Promise return multiple values

I use the following code to return promise which is working OK.
The promise return the data value
run: () => {
return new Promise((resolve, reject) => {
....
}).then((data) => {
let loginApi = data[0]
let test = 1;
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
resolve(data )
}
})
});
I call it
module.run()
.then((data) => {
And I was able to get the data.
now I want to return also value test in the resolve, how should I do it?
I try to add it like this
resolve({data,test});
resolve([data,test]);
with call like
module.run()
.then({data,test}) => {
without success(test is empty), I read about spread but this is the only option?
I use ES6 with bluebird latest version
If you are using promise chain, in promise chain you have then->then->catch->... format. Always return Promise.resolve or Promise.reject. Promise.resolve will give success result for next then block and Promise.reject will go to next catch block.
var module = {
run: () => {
return new Promise((resolve, reject) => {
// ....
resolve('promise resolved')
}).then((data) => {
let loginApi = data[0]
let test = 1;
return Promise.resolve({data,test})
}).catch((err) => {
if (err.statusCode === 302) {
var data = url.parse(err.response.headers.location, true)
return Promise.resolve({data, test});
}
return Promise.reject(err);
})
}
};
module.run().then(({data, test}) => {
console.log(data, test);
})

Cannot return opened realm instance in writing state in a resolved promise

I am trying to create a realm helper function to return an open and writeable realm instance...I am not sure why this is not working? I am getting error:
Error: Can only delete objects within a transaction.
This indicates that the (realm) in the resolve is closing/becoming unwritable when it is returned via the resolve.
import * as Realm from "realm";
// Realm object is returned but is not ready for writing?.
export const RealmHelper = {
realmWrite(schema: any) : Promise<Realm> {
const promise = new Promise<Realm>((resolve, reject) => {
Realm.open({schema: schema}).then((realm: Realm) => {
realm.write(() => {
resolve(realm);
});
}).catch((error) => reject(error));
});
return promise;
}
};
I am calling the helper with:
return RealmHelper.realmWrite([UserSchema]).then((realm: Realm) => {
realm.delete(this); // from within a UserSchema
return true;
});
This more ugly and verbose way works fine:
function deleteUser() {
const promise = new Promise<boolean>((resolve, reject) => {
Realm.open({schema: [UserSchema]}).then((realm: Realm) => {
realm.write(() => {
console.log("deleting: user ", this);
realm.delete(this);
resolve(true);
});
}).catch((error) => reject(error));
});
return promise;
}
When you resolve the promise in this block
Realm.open({schema: schema}).then((realm: Realm) => {
realm.write(() => {
resolve(realm);
});
}).catch((error) => reject(error));
you're also immediately exiting the scope in which the write transaction happen. Consider this:
Realm.open({schema: schema}).then((realm: Realm) => {
realm.write(() => {
console.log("before resolve");
resolve(realm);
console.log("after resolve");
});
}).catch((error) => reject(error));
// use the helper
return RealmHelper.realmWrite([UserSchema]).then((realm: Realm) => {
console.log("before delete");
realm.delete(this); // from within a UserSchema
console.log("after delete");
return true;
});
// OUTPUT:
before resolve
after resolve
before delete
after delete
I haven't tested this code, but I expect you'll see something like it.
Fixed by adding a callback that would be called before the resolve in the write callback. Here's the solution that works for me:
export const RealmHelper = {
realmWrite<T>(schema: any, func: (realm: Realm) => T) : Promise<T> {
const promise = new Promise<T>((resolve, reject) => {
Realm.open({schema: schema}).then((realm: Realm) => {
realm.write(() => {
resolve(func(realm));
});
}).catch((error) => reject(error));
});
return promise;
}
};
Can be called using:
RealmHelper.realmWrite([UserSchema], (realm) => realm.create<User>("User", this));

Resolve Javascript Promise outside the Promise constructor scope

I have been using ES6 Promise.
Ordinarily, a Promise is constructed and used like this
new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
But I have been doing something like below to take the resolve outside for the sake of flexibility.
var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) {
outsideResolve = resolve;
outsideReject = reject;
});
And later
onClick = function(){
outsideResolve();
}
This works fine, but is there an easier way to do this? If not, is this a good practice?
simple:
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
Bit late to the party here, but another way to do it would be to use a Deferred object. You essentially have the same amount of boilerplate, but it's handy if you want to pass them around and possibly resolve outside of their definition.
Naive Implementation:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject)=> {
this.reject = reject
this.resolve = resolve
})
}
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(()=> {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(result => {
console.log(result) // 42
})
ES5 Version:
function Deferred() {
var self = this;
this.promise = new Promise(function(resolve, reject) {
self.reject = reject
self.resolve = resolve
})
}
function asyncAction() {
var dfd = new Deferred()
setTimeout(function() {
dfd.resolve(42)
}, 500)
return dfd.promise
}
asyncAction().then(function(result) {
console.log(result) // 42
})
No, there is no other way to do this - the only thing I can say is that this use case isn't very common. Like Felix said in the comment - what you do will consistently work.
It's worth mentioning that the reason the promise constructor behaves this way is throw safety - if an exception you did not anticipate happens while your code is running inside the promise constructor it will turn into a rejection, this form of throw safety - converting thrown errors to rejections is important and helps maintain predictable code.
For this throw safety reason, the promise constructor was chosen over deferreds (which are an alternative promise construction way that do allow what you're doing) - as for best practices - I'd pass the element and use the promise constructor instead:
var p = new Promise(function(resolve, reject){
this.onclick = resolve;
}.bind(this));
For this reason - whenever you can use the promise constructor over exporting the functions - I recommend you do use it. Whenever you can avoid both - avoid both and chain.
Note, that you should never use the promise constructor for things like if(condition), the first example could be written as:
var p = Promise[(someCondition)?"resolve":"reject"]();
I liked #JonJaques answer but I wanted to take it a step further.
If you bind then and catch then the Deferred object, then it fully implements the Promise API and you can treat it as promise and await it and such.
⚠️ Editor's Note: I don't recommend this kind of pattern anymore since at the time of writing, Promise.prototype.finally was not a thing yet, then it became a thing… This could happen to other methods so I recommend you augment the promise instance with resolve and reject functions instead:
function createDeferredPromise() {
let resolve
let reject
const promise = new Promise((thisResolve, thisReject) => {
resolve = thisResolve
reject = thisReject
})
return Object.assign(promise, {resolve, reject})
}
Go upvote someone else's answer.
class DeferredPromise {
constructor() {
this._promise = new Promise((resolve, reject) => {
// assign the resolve and reject functions to `this`
// making them usable on the class instance
this.resolve = resolve;
this.reject = reject;
});
// bind `then` and `catch` to implement the same interface as Promise
this.then = this._promise.then.bind(this._promise);
this.catch = this._promise.catch.bind(this._promise);
this.finally = this._promise.finally.bind(this._promise);
this[Symbol.toStringTag] = 'Promise';
}
}
const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
deferred.resolve('whoa!');
}, 2000);
async function someAsyncFunction() {
const value = await deferred;
console.log(value);
}
someAsyncFunction();
A solution I came up with in 2015 for my framework. I called this type of promises Task
function createPromise(handler){
var resolve, reject;
var promise = new Promise(function(_resolve, _reject){
resolve = _resolve;
reject = _reject;
if(handler) handler(resolve, reject);
})
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
// create
var promise = createPromise()
promise.then(function(data){ alert(data) })
// resolve from outside
promise.resolve(200)
Accepted answer is wrong. It's pretty easy using scope and references, though it may make Promise purists angry:
const createPromise = () => {
let resolver;
return [
new Promise((resolve, reject) => {
resolver = resolve;
}),
resolver,
];
};
const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);
We are essentially grabbing the reference to the resolve function when the promise is created, and we return that so it can be set externally.
In one second the console will output:
> foo
A helper method would alleviate this extra overhead, and give you the same jQuery feel.
function Deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
Usage would be
const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
return promise;
Which is similar to jQuery
const dfd = $.Deferred();
displayConfirmationDialog({
confirm: dfd.resolve,
cancel: dfd.reject
});
return dfd.promise();
Although, in a use case this simple, native syntax is fine
return new Promise((resolve, reject) => {
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
});
I'm using a helper function to create what I call a "flat promise" -
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
And I'm using it like so -
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
See full working example -
function flatPromise() {
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
function doSomethingAsync() {
// Get your promise and callbacks
const { resolve, reject, promise } = flatPromise();
// Do something amazing...
setTimeout(() => {
resolve('done!');
}, 500);
// Pass your promise to the world
return promise;
}
(async function run() {
const result = await doSomethingAsync()
.catch(err => console.error('rejected with', err));
console.log(result);
})();
Edit:
I have created an NPM package called flat-promise and the code is also available on GitHub.
Just in case somebody came looking for a typescript version of a util simplifying this task:
export const deferred = <T>() => {
let resolve!: (value: T | PromiseLike<T>) => void;
let reject!: (reason?: any) => void;
const promise = new Promise<T>((res, rej) => {
resolve = res;
reject = rej;
});
return {
resolve,
reject,
promise,
};
};
This can be used eg. like:
const {promise, resolve} = deferred<string>();
promise.then((value) => console.log(value)); // nothing
resolve('foo'); // console.log: foo
You can wrap the Promise in a class.
class Deferred {
constructor(handler) {
this.promise = new Promise((resolve, reject) => {
this.reject = reject;
this.resolve = resolve;
handler(resolve, reject);
});
this.promise.resolve = this.resolve;
this.promise.reject = this.reject;
return this.promise;
}
promise;
resolve;
reject;
}
// How to use.
const promise = new Deferred((resolve, reject) => {
// Use like normal Promise.
});
promise.resolve(); // Resolve from any context.
I find myself missing the Deferred pattern as well in certain cases. You can always create one on top of a ES6 Promise:
export default class Deferred<T> {
private _resolve: (value: T) => void = () => {};
private _reject: (value: T) => void = () => {};
private _promise: Promise<T> = new Promise<T>((resolve, reject) => {
this._reject = reject;
this._resolve = resolve;
})
public get promise(): Promise<T> {
return this._promise;
}
public resolve(value: T) {
this._resolve(value);
}
public reject(value: T) {
this._reject(value);
}
}
Many of the answers here are similar to the last example in this article.
I am caching multiple Promises, and the resolve() and reject() functions can be assigned to any variable or property. As a result I am able to make this code slightly more compact:
function defer(obj) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
}
Here is a simplified example of using this version of defer() to combine a FontFace load Promise with another async process:
function onDOMContentLoaded(evt) {
let all = []; // array of Promises
glob = {}; // global object used elsewhere
defer(glob);
all.push(glob.promise);
// launch async process with callback = resolveGlob()
const myFont = new FontFace("myFont", "url(myFont.woff2)");
document.fonts.add(myFont);
myFont.load();
all.push[myFont];
Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
glob.resolve();
}
function runIt() {} // runs after all promises resolved
Update: 2 alternatives in case you want to encapsulate the object:
function defer(obj = {}) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
return obj;
}
let deferred = defer();
and
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
let deferred = new Deferred();
Our solution was to use closures to store the resolve/reject functions and additionally attach a function to extend the promise itself.
Here is the pattern:
function getPromise() {
var _resolve, _reject;
var promise = new Promise((resolve, reject) => {
_reject = reject;
_resolve = resolve;
});
promise.resolve_ex = (value) => {
_resolve(value);
};
promise.reject_ex = (value) => {
_reject(value);
};
return promise;
}
And using it:
var promise = getPromise();
promise.then(value => {
console.info('The promise has been fulfilled: ' + value);
});
promise.resolve_ex('hello');
// or the reject version
//promise.reject_ex('goodbye');
Yes, you can. By using the CustomEvent API for the browser environment. And using an event emitter project in node.js environments. Since the snippet in the question is for the browser environment, here is a working example for the same.
function myPromiseReturningFunction(){
return new Promise(resolve => {
window.addEventListener("myCustomEvent", (event) => {
resolve(event.detail);
})
})
}
myPromiseReturningFunction().then(result => {
alert(result)
})
document.getElementById("p").addEventListener("click", () => {
window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>
I hope this answer is useful!
Thanks to everyone who posted in this thread. I created a module that includes the Defer() object described earlier as well as a few other objects built upon it. They all leverage Promises and the neat Promise call-back syntax to implement communication/event handling within a program.
Defer: Promise that can be resolved failed remotely (outside of its body)
Delay: Promise that is resolved automatically after a given time
TimeOut: Promise that fails automatically after a given time.
Cycle: Re-triggerable promise to manage events with the Promise syntax
Queue: Execution queue based on Promise chaining.
rp = require("openpromise")
https://github.com/CABrouwers/openpromise
https://www.npmjs.com/package/openpromise
Class version, in Typescript :
export class Deferred<T> {
public readonly promise: Promise<T>
private resolveFn!: (value: T | PromiseLike<T>) => void
private rejectFn!: (reason?: any) => void
public constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolveFn = resolve
this.rejectFn = reject
})
}
public reject(reason?: any): void {
this.rejectFn(reason)
}
public resolve(param: T): void {
this.resolveFn(param)
}
}
I wrote a small lib for this. https://www.npmjs.com/package/#inf3rno/promise.exposed
I used the factory method approach others wrote, but I overrode the then, catch, finally methods too, so you can resolve the original promise by those as well.
Resolving Promise without executor from outside:
const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");
Racing with the executor's setTimeout from outside:
const promise = Promise.exposed(function (resolve, reject){
setTimeout(function (){
resolve("I almost fell asleep.")
}, 100000);
}).then(console.log);
setTimeout(function (){
promise.resolve("I don't want to wait that much.");
}, 100);
There is a no-conflict mode if you don't want to pollute the global namespace:
const createExposedPromise = require("#inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
I made a library called manual-promise that functions as a drop in replacement for Promise. None of the other answers here will work as drop in replacements for Promise, as they use proxies or wrappers.
yarn add manual-promise
npn install manual-promise
import { ManualPromise } from "manual-promise";
const prom = new ManualPromise();
prom.resolve(2);
// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
// ... code
});
new ManualPromise() instanceof Promise === true
https://github.com/zpxp/manual-promise#readme
Just another solution to resolve Promise from the outside
class Lock {
#lock; // Promise to be resolved (on release)
release; // Release lock
id; // Id of lock
constructor(id) {
this.id = id
this.#lock = new Promise((resolve) => {
this.release = () => {
if (resolve) {
resolve()
} else {
Promise.resolve()
}
}
})
}
get() { return this.#lock }
}
Usage
let lock = new Lock(... some id ...);
...
lock.get().then(()=>{console.log('resolved/released')})
lock.release() // Excpected 'resolved/released'
How about creating a function to hijack the reject and return it ?
function createRejectablePromise(handler) {
let _reject;
const promise = new Promise((resolve, reject) => {
_reject = reject;
handler(resolve, reject);
})
promise.reject = _reject;
return promise;
}
// Usage
const { reject } = createRejectablePromise((resolve) => {
setTimeout(() => {
console.log('resolved')
resolve();
}, 2000)
});
reject();
I've put together a gist that does that job: https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13
here's how you should use it:
import ExternalizedPromiseCreator from '../externalized-promise';
describe('ExternalizedPromise', () => {
let fn: jest.Mock;
let deferredFn: jest.Mock;
let neverCalledFn: jest.Mock;
beforeEach(() => {
fn = jest.fn();
deferredFn = jest.fn();
neverCalledFn = jest.fn();
});
it('resolve should resolve the promise', done => {
const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());
externalizedPromise
.promise
.then(() => deferredFn())
.catch(() => neverCalledFn())
.then(() => {
expect(deferredFn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
done();
});
expect(fn).toHaveBeenCalled();
expect(neverCalledFn).not.toHaveBeenCalled();
expect(deferredFn).not.toHaveBeenCalled();
externalizedPromise.resolve();
});
...
});
As I didn't find what I was looking for, I will share what I actually wanted to achieve when I ended in this question.
Scenario: I have 3 different API's with same possible response and therefore I would like to handle the completion and error handling of the promises in a single function. This is what I did:
Create a handler function:
private handleHttpPromise = (promise: Promise<any>) => {
promise
.then((response: any) => {
// do something with the response
console.log(response);
})
.catch((error) => {
// do something with the error
console.log(error);
});
};
Send your promises to the created handler
switch (method) {
case 'get': {
this.handleHttpPromise(apiService.get(url));
break;
}
case 'post': {
if (jsonData) {
this.handleHttpPromise(apiService.post(url, jsonData));
}
break;
}
// (...)
}
I would like to share something different, an extension to this topic.
Sometimes you want a "task promise" to be automatically re-created at the same address (property or variable) when it resolves. It's possible to create an outside resolver that does just that.
Example of a recurring promise with an external resolver. Whenever the resolver is called, a new promise is created at the same address/variable/property.
let resolvePromise;
let thePromise;
const setPromise = (resolve) => {
resolvePromise = () => {
resolve();
thePromise = new Promise(setPromise);
}
}
thePromise = new Promise(setPromise);
(async () => {
let i = 0;
while (true) {
let msg = (i % 2 === 0) ? 'Tick' : 'Tock';
document.body.innerHTML = msg;
setTimeout(resolvePromise, 1000);
await thePromise;
i++;
}
})();
https://jsfiddle.net/h3zvw5xr
If (like me) you don't like augmenting native instances, nor unwieldy ".promise" properties ... but do love proxies and mangling classes, then this one is for you:
class GroovyPromise {
constructor() {
return new Proxy(new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
}), {
get: (target, prop) =>
this[prop] || target[prop].bind(target),
});
}
}
Used like so:
const groovypromise = new GroovyPromise();
setTimeout(() => groovypromise.resolve('groovy'), 1000);
console.log(await groovypromise);
Of course you can also rename the class to something dull like "Deferred"
For fun, you also combine a promise into a self-resolvable function:
function Resolver() {
let resolve;
const promise = new Promise(r => resolve = r);
return new Proxy(resolve, {
get: (_, prop) => promise[prop].bind(promise)
});
}
const resolve = Resolver();
(async () => {
resolve
.then(value => console.log('thenable:', value))
.finally(() => console.log('finally'));
const value = await resolve;
console.log('awaitable:', value);
})()
resolve('test');
// thenable: test
// finally
// awaitable: test
first enable --allow-natives-syntax on browser or node
const p = new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
onClick = function () {
%ResolvePromise(p, value)
}

Categories