Reputation: 91183
In Node.js, how do you chain class methods together, when it's best practice to use callbacks?
In my PHP days, I would typically do something like this:
class MyClass {
function get() {
// fetch some data from an API endpoint
return this;
}
function set(property, value) {
// Set the property to some value on the object
return this;
}
function save() {
// Save the data to database
return this;
}
}
$myObject = new MyClass();
$myObject->set('something','taco')->save();
This very common OO approach allowed you to chain methods together as many times as you wanted.
When working with Node.js, can you still somehow do chaining similar to this? Or do you just end up in callback hell? Every single "chain" is a nested callback?
Or do I simply need to wrap my script in Promises?
new Promise(function(resolve, reject){
var myObject = new MyClass();
myObject.set('something','taco');
resolve(myObject);
}).then(function(myObject){
myObject.save();
});
Is that how you are supposed to do it? Is there any way to integrate this more deeply into my class so I don't have to wrap it in promises every time? I've seen some libraries have sort of a "promise-mode" like https://github.com/sindresorhus/got but after looking at the code I'm still not exactly sure how they did it.
Upvotes: 4
Views: 1159
Reputation: 316
Your method could be something like this:
(async function() {
class MyClass {
constructor() {
this._lastPromise = Promise.resolve();
this.a = 0;
}
set(property, value) {
var self = this;
return new Promise(async (resolve, reject) => {
console.log("self.%s set to: %s", property, value);
await new Promise((resolve, reject) => {
setTimeout(resolve, 5000);
});
self[property] = value;
resolve(self);
});
}
save() {
var self = this;
return new Promise(function(resolve, reject) {
console.log(self.a + " Saved");
resolve(self);
});
}
}
var myObject = new MyClass();
console.log("before");
await myObject
.set("a", "1")
.then(self => self.save())
.then(self => self.set("a", "5"))
.then(self => self.save());
console.log("after");
})();
Upvotes: 0
Reputation: 34627
You can store a special private member variable __lastPromise
. Initially it will be resolved by default. But then any function that does a task will update it with a return promise. And also the function itself will only do its task after the previous promise stored has been resolved.
Like this:
save() {
// only after __lastPromise has resolved
var newPromise = __lastPromise.then(function() {
// Do stuff here
});
// update __lastPromise with the newly returned promise
this.__lastPromise = newPromise;
// return this for regular chaining
return this;
}
Complete class:
class MyClass {
constructor(){
this.__lastPromise = Promise.resolve();
this.a = 0;
}
set(property, value) {
var self = this;
self.__lastPromise = self.__lastPromise.then(function() {
return new Promise(function(resolve, reject) {
console.log('self.%s set to: ', property, value);
self[property] = value;
resolve();
});
});
return self;
}
save() {
var self = this;
self.__lastPromise = self.__lastPromise.then(function() {
return new Promise(function(resolve, reject) {
console.log('Saved');
resolve();
});
});
return self;
}
}
var myObject = new MyClass();
myObject.set('a', '1').save().set('a', '2').save();
this.a set to: 1
Saved
this.a set to: 2
Saved
Upvotes: 1
Reputation: 8666
You chain using return this
for synchronous calls.
You chain using Promise
or callbacks for asynchronous calls.
class MyClass {
get() {
return new Promise((resolve, reject)=> {
// Make Request and call resolve( result ) or reject( err )
});
}
set( property, value ) {
this[property] = value;
return this;
}
save() {
return new Promise((resolve, reject)=> {
// Make Request and call resolve( result ) or reject( err )
});
}
}
var myObject = new MyClass();
myObject
.set('something', 'taco')
.save()
// resolve handler
.then(( saveResult )=> {
console.log( saveResult );
return myObject.get();
})
// resolve handler
.then(( getResult )=> {
console.log( getResult );
})
// reject handler
.catch(( error )=> { });
Upvotes: 4