Skip to content

Instantly share code, notes, and snippets.

@pete-rai
Created January 13, 2021 11:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pete-rai/fb197af2b0ec7838216c6728c3ba2468 to your computer and use it in GitHub Desktop.
Save pete-rai/fb197af2b0ec7838216c6728c3ba2468 to your computer and use it in GitHub Desktop.
Method chaining is useful way to construct readable software. It is a process where each method returns an object, allowing the calls to be chained together in a single statement without requiring variables to store the intermediate results. However, how can we achieve this in the face of JavaScript’s asynchronous function? Here is a simple solu…
/*
Method chaining is useful way to construct readable software. It is a process
where each method returns an object, allowing the calls to be chained together
in a single statement without requiring variables to store the intermediate
results. However, how can we achieve this in the face of JavaScript’s
asynchronous function? Here is a simple solution for this tricky issue.
*/
// --- Engine class - methods for sequencing operations
class Engine {
// --- constructor
constructor(tracing = false) {
this._chain = Promise.resolve(); // roots the chain
this._tracing = tracing;
}
// --- add a method to the operation chain
chain(callback) {
return this._chain = this._chain.then(callback);
}
// --- promise then handler
then(callback) {
callback(this._chain);
}
// --- outputs a trace message
_trace(text) {
if (this._tracing) console.log((new Date().toISOString()) + ': ' + text);
}
trace(text) {
this.chain(async () => {
this._trace(text);
});
return this;
}
// --- sets the trace mode
tracing(mode) {
this._tracing = mode;
}
// --- waits for the given time
_wait(millisecs) {
this._trace(`waiting for ${millisecs}ms...`);
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, millisecs);
})
.finally(() => this._trace('wait over'));
}
wait(millisecs) {
this.chain(async () => {
await this._wait(millisecs);
});
return this;
}
}
// --- an example class where you can provide the actual methods
class SomeClass extends Engine {
// --- constructor
constructor() {
// your stuff here
super(true); // tracing on
}
// --- an example async method that does NOT return a result
_func1(params) {
this._trace('func1 - started');
return new Promise((resolve) => { // using setTimeout here is just an example - provide your own async function such as a web request or a database lookup
setTimeout(() => {
resolve();
}, Math.random() * 1000);
})
.finally(() => this._trace('func1 - completed'));
}
func1(params) {
this.chain(async () => {
return await this._func1(params);
});
return this;
}
// --- an example async method that does NOT return a result
_func2(params) {
this._trace('func2 - started');
return new Promise((resolve) => { // using setTimeout here is just an example - provide your own async function such as a web request or a database lookup
setTimeout(() => {
resolve();
}, Math.random() * 1000);
})
.finally(() => this._trace('func2 - completed'));
}
func2(params) {
this.chain(async () => {
return await this._func2(params);
});
return this;
}
// --- an example async method that DOES return a result
_func3(params) {
this._trace('func3 - started');
return new Promise((resolve) => { // using setTimeout here is just an example - provide your own async function such as a web request or a database lookup
setTimeout(() => {
resolve();
}, Math.random() * 1000);
})
.then(() => 'your returned results will go here')
.finally(() => this._trace('func3 - completed'));
}
func3(params) {
this.chain(async () => {
return await this._func3(params);
});
return this;
}
}
// --- example usage - i am using async/await, but standard promises will also work
async function example() {
var test = new SomeClass();
test.func1().trace('message 1').func2().wait(1000).trace('message 2').wait(500).func1().trace('message 3'); // chain that does NOT end in a returned result
let result = await test.func1().func2().func3(); // chain that DOES end in a returned result - func3
console.log(result);
}
example();
/*
This code results in the following output:
2021-01-13T11:43:31.086Z: func1 - started
2021-01-13T11:43:31.230Z: func1 - completed
2021-01-13T11:43:31.230Z: message 1
2021-01-13T11:43:31.230Z: func2 - started
2021-01-13T11:43:31.793Z: func2 - completed
2021-01-13T11:43:31.794Z: waiting for 1000ms...
2021-01-13T11:43:32.797Z: wait over
2021-01-13T11:43:32.797Z: message 2
2021-01-13T11:43:32.797Z: waiting for 500ms...
2021-01-13T11:43:33.298Z: wait over
2021-01-13T11:43:33.298Z: func1 - started
2021-01-13T11:43:33.871Z: func1 - completed
2021-01-13T11:43:33.872Z: message 3
2021-01-13T11:43:33.872Z: func1 - started
2021-01-13T11:43:34.674Z: func1 - completed
2021-01-13T11:43:34.674Z: func2 - started
2021-01-13T11:43:35.523Z: func2 - completed
2021-01-13T11:43:35.524Z: func3 - started
2021-01-13T11:43:36.133Z: func3 - completed
your returned results will go here
*/
@pete-rai
Copy link
Author

If you just want a more simple JavaScript operation sequencer - see my other gist https://gist.github.com/pete-rai/fecc4de7ebe6c8c18418aef933e5a3bf

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment