I want to make this code work
$('start') // push initial value to an array
.addItem(2) // add another value
.printAll() // print everything in array
.delay(2000) // wait x seconds
.addItem(5)
.printAll()
.start(); // execute all the commands before this line
I created a class with data array to hold the items. and steps array to hold the operations. Then i used chainable promises to execute them. is that the best way to do what i'm trying to achieve? Do I really need to store the operations in an array?
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = (v)=>this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v=>console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let start = Promise.resolve();
this.steps.forEach(({fun, args})=>{
start = start.then(()=>{
return new Promise(resolve=>{
if (fun === window.setTimeout) {
setTimeout(resolve, ...args);
} else {
fun.apply(this, args);
resolve();
}
}
);
}
);
}
);
return this;
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
Although your question belongs to https://codereview.stackexchange.com/ according to me, I tried to think of another implementation without Promises. It works with closures and callbacks only:
class Sync {
constructor() {}
init(startValue) {
this.data = [startValue];
this.steps = [];
return this;
}
addItem(value) {
const append = v => this.data.push(v);
this._addStep(append, value);
return this;
}
printAll() {
this._addStep(v => console.log(v.join()), this.data);
return this;
}
delay(value) {
this._addStep(window.setTimeout, value);
return this;
}
_addStep(fun, ...args) {
this.steps.push({
fun,
args
});
}
start() {
let finalFunction;
this.steps.reverse().forEach(({ fun, args }) => {
if (fun === window.setTimeout) {
finalFunction = finalFunction ? encloseFunctionWithArgs(null, null, finalFunction, next => setTimeout(next, ...args)) : null;
} else {
finalFunction = encloseFunctionWithArgs(fun, args, finalFunction);
}
});
finalFunction();
return this;
}
}
function encloseFunctionWithArgs(fun, args, next, trigger) {
return function () {
if (fun)
fun(args);
if (next)
trigger ? trigger(next) : next();
}
}
const lib = new Sync();
const $ = lib.init.bind(lib);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();
EDIT
I finally managed to achieve what you want:
const base = {
init(startValue) {
this.promise = new Promise(resolve => this.start = resolve).then(() => [startValue]);
return this;
},
addItem(value) {
this.promise = this.promise.then(v => [...v, value]);
return this;
},
printAll() {
this.promise.then(v => v.join()).then(console.log);
return this;
},
delay(value) {
this.promise = this.promise.then(v => new Promise(resolve => setTimeout(() => resolve(v), value)));
return this;
}
}
const factory = base => arg => Object.create(base).init(arg);
const $ = factory(base);
$('start')
.addItem(2)
.printAll()
.delay(2000)
.addItem(5)
.printAll()
.start();