Javascript Promise within Class - javascript

Fun with promises today ... I'm trying to figure out why I can't refer to this.promise to resolve it with another method, it's always null.
Any enlightenment is appreciated :)
export default class Example {
constructor() {
this.promise = null
}
test() {
this.promise = new Promise((resolve, reject) => {
this.something()
}).then(resolve => {
// resolved, do something else
}).catch(reject => {
console.log('reject', reject)
})
}
something() {
this.promise.resolve()
}
}

You're trying to use this.promise in something before it's value changes from null. Another way to do this is to pass the resolve as an argument:
class Example {
constructor() {
this.promise = null;
}
test() {
this.promise = new Promise((resolve, reject) => {
this.something(resolve);
}).then(result => {
console.log('resolve', result);
}).catch(error => {
console.log('reject', error);
});
}
something(resolve) {
resolve(1);
}
}
const example = new Example();
example.test();

Related

JS Promise resolve function not working (while reject function does)

I have a simple async function in my JavaScript code:
export default class DanceAnimationCanvas extends AnimationCanvas {
async initTimeline(): Promise<any> {
return new Promise((resolve, reject) => {
const app = this.app;
const timeline = new TimelineLite();
app.loader.add(this.imageSrc).load((loader, resources) => {
const mainImage = new PIXI.Sprite(resources[this.imageSrc].texture);
app.stage.addChild(mainImage);
timeline.to(mainImage, { rotation: 10, duration: 100 });
console.log('resolve');
resolve(timeline);
});
});
}
}
The message 'resolve' is logged to the console, so I am sure that the resolve function is called. However, this code does not work:
async getCanvas(): Promise<HTMLCanvasElement> {
if (!this.timeline) {
try {
this.timeline = await this.initTimeline();
}
catch (error) {
console.log({ error });
}
}
console.log('timeline', this.timeline);
return this.app.view;
}
This function never resolves (the HTMLCanvasElement is never returned). Also the catch function is not called, so error is not logged.
The weird thing is that it DOES work when I use the reject function instead of the resolve function (which is wrong, I know):
export default class DanceAnimationCanvas extends AnimationCanvas {
async initTimeline(): Promise<any> {
return new Promise((resolve, reject) => {
const app = this.app;
const timeline = new TimelineLite();
app.loader.add(this.imageSrc).load((loader, resources) => {
const mainImage = new PIXI.Sprite(resources[this.imageSrc].texture);
app.stage.addChild(mainImage);
timeline.to(mainImage, { rotation: 10, duration: 100 });
reject(timeline);
});
});
}
}
async getCanvas(): Promise<HTMLCanvasElement> {
if (!this.timeline) {
try {
this.timeline = await this.initTimeline();
}
catch (timeline) {
this.timeline = timeline;
}
}
console.log('timeline', this.timeline);
return this.app.view;
}
Everything now works as I would want it to, but off course this is not the right way to do it. I'm probably not seeing something, but I've been at this for an hour and just cannot see why this happens...

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);

Extending a Promise in 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)

Promise.all is not displaying the data

I am new to promises and I want to return the data from classes as promises. I have two classes with two function in each class. What I am doing each function is returning a promise from a class, but I am unable to execute promise.all. Below is the code
Class1
class TestClass {
firstPromise() {
return new Promise((resolve, reject) => {
resolve('return first promise')
})
}
secondPromise() {
return new Promise((resolve, reject) => {
resolve('return second promise')
})
}
}
module.exports.TestClass = TestClass;
Class2
class TestClass1 {
firstPromise() {
return new Promise((resolve, reject) => {
resolve('return first promise')
})
}
secondPromise() {
return new Promise((resolve, reject) => {
resolve('return second promise')
})
}
}
module.exports.TestClass1 = TestClass1;
Main function
let data = req.body;
let test1Object;
let testObject;
let testParam;
let testParam1;
if (data.hasOwnProperty('param1')) {
test1Object = new test1.TestClass1();
test1Object.firstPromise().then((data)=>{
testParam1 = test1Object.secondPromise();
});
}
if (data.hasOwnProperty('param2')) {
testObject = new test.TestClass();
testObject.firstPromise().then((data)=>{
testParam = testObject.secondPromise()
});
}
Promise.all([testParam,testParam1]).then((data)=>{
console.log(data)
});
it displays
[ undefined, undefined ]
When you're executing Promise.all(), the promises are not yet resolved, so testParam and testParam1 are undefined. I think you should assign the first promise to testParam1:
testParam1 = test1Object.firstPromise().then(data => test1Object.secondPromise());
and similarly the second to testParam:
testParam = testObject.firstPromise().then(data => testObject.secondPromise());

Categories