I have a custom Promise2 class that extends Promise class to allow early settling. It uses my custom Timer class to check the progress of a simulated activity through another timer t1. In my example, p1 does an early settling but the problem is with the p1.then( doesn't recognize the onfulfilled callback as a function.
I suspected I have to override then() and call the super.then() but it didn't work. By the way, the timed executor callback inside super() is just a workaround to make this accessible. Any ideas on what's lacking in my Promise2 class?
JavaScript Code
'use strict';
const p1 = new Promise2(
(resolve, reject) => {
const t1 = Timer.create(
() => {
resolve('Promise resolved.');
// reject(new Error('Promise rejected.'));
},
3000,
);
return { timer: t1 };
},
);
Timer.create(
() => {
const { isCompleted } = p1.return.timer;
const { progress } = p1.return.timer;
if (isCompleted === false) {
console.log(`Promise: ${progress} %`);
if (progress > 50) {
p1.resolve('Early resolve.');
// p1.reject(new Error('Early reject.'));
p1.return.timer.stop();
}
}
},
250,
true,
16,
);
// p1.promise.then(
p1.then(
(value) => {
console.log('__resolve__');
console.log(value);
},
)
.catch(
(reason) => {
console.log('__catch__');
console.log(reason);
},
);
Promise2 Class
class Promise2 extends Promise {
constructor(executor = null) {
super(
(resolve, reject) => {
Timer.create(
() => {
this.resolve = resolve;
this.reject = reject;
this.return = executor(resolve, reject);
},
1);
},
);
// this.promise = new Promise(
// (resolve, reject) => {
// this.resolve = resolve;
// this.reject = reject;
// this.return = executor(resolve, reject);
// },
// );
}
static create(executor = null) {
return new Promise2(...arguments);
}
}
Timer Class
class Timer {
constructor(workload = null, milliseconds = 1000, isAutostart = true, repeat = 1, isInterval = false) {
this.workload = workload;
this.milliseconds = milliseconds;
this.isAutostart = isAutostart;
this.repeat = repeat;
this.isInterval = isInterval;
this.startTime = 0;
this.endTime = 0;
this.timeLeft = milliseconds;
this.timeoutId = 0;
this.progress = 0;
this.isCompleted = false;
this.endTimeActual = 0;
this.repeatLeft = repeat;
this.isPaused = false;
this.subTimers = [];
if (isAutostart === true) {
this.start();
}
}
start(thisArg = this) {
thisArg.startTime = Date.now();
thisArg.endTime = thisArg.startTime + thisArg.milliseconds;
const timeoutEndTime = Date.now();
thisArg.watch(thisArg.workload, timeoutEndTime, thisArg);
}
watch(workload = null, timeoutEndTime = 0, thisArg = this) {
if (thisArg.isPaused === true) {
return;
}
const timeoutLag = Date.now() - timeoutEndTime;
thisArg.timeLeft = thisArg.endTime - Date.now() - timeoutLag;
if (thisArg.timeLeft > 0) {
thisArg.progress = 100 - ((thisArg.timeLeft / thisArg.milliseconds) * 100);
const inProgress = 100 - thisArg.progress;
const tick = thisArg.timeLeft / inProgress;
timeoutEndTime = Date.now() + tick;
thisArg.timeoutId = setTimeout(thisArg.watch, tick, thisArg.workload, timeoutEndTime, thisArg);
return;
}
thisArg.stop(thisArg);
workload();
if (thisArg.repeatLeft > 0) {
thisArg.isCompleted = false;
thisArg.start();
}
if (thisArg.isInterval === true) {
thisArg.repeatLeft = 1;
}
if (thisArg.subTimers.length > 0) {
thisArg.subTimers.forEach(
(timer) => {
if (timer.isAutostart === true) {
timer.start();
}
},
);
}
}
stop(thisArg = this) {
clearTimeout(thisArg.timeoutId);
thisArg.isCompleted = true;
thisArg.endTimeActual = Date.now();
thisArg.repeatLeft -= 1;
if (thisArg.isInterval === true) {
thisArg.repeatLeft = 0;
}
}
restart(thisArg = this) {
thisArg.stop();
thisArg.startTime = 0;
thisArg.endTime = 0;
thisArg.timeLeft = thisArg.milliseconds;
thisArg.timeoutId = 0;
thisArg.progress = 0;
thisArg.isCompleted = false;
thisArg.endTimeActual = 0;
thisArg.repeatLeft = thisArg.repeat;
thisArg.start();
}
pause(thisArg = this) {
thisArg.isPaused = true;
}
resume(thisArg = this) {
thisArg.isPaused = false;
const timeoutEndTime = Date.now();
thisArg.watch(thisArg.workload, timeoutEndTime, thisArg);
}
static create(workload = null, milliseconds = 1000, isAutostart = true, repeat = 1, isInterval = false) {
return new Timer(...arguments);
}
static chain(timers = []) {
const timerReferences = Timer.chainWalk(timers);
if (timerReferences[0].isAutostart === true) {
timerReferences[0].start();
}
return timerReferences;
}
static chainWalk(timers = [], timerReferences = [], nextTimer = null) {
if (timers.length === 0) {
return timerReferences;
}
if (timerReferences.length === 0) {
timers = [...timers];
}
const timer = timers.shift();
const {
workload = null,
milliseconds = 1000,
isAutostart = true,
repeat = 1,
isInterval = false,
} = timer;
const newTimer = new Timer(workload, milliseconds, false, repeat, isInterval);
newTimer.isAutostart = isAutostart;
if (timerReferences.length === 0) {
timerReferences.push(newTimer);
[nextTimer] = timerReferences;
} else {
nextTimer.subTimers.push(newTimer);
[nextTimer] = nextTimer.subTimers;
}
timerReferences = Timer.chainWalk(timers, timerReferences, nextTimer);
return timerReferences;
}
static tree(timers = []) {
const timerReferences = Timer.treeWalk(timers);
timerReferences.forEach(
(reference) => {
if (reference.isAutostart === true) {
reference.start();
}
},
);
return timerReferences;
}
static treeWalk(timers = []) {
const timerReferences = [];
timers.forEach(
(timer) => {
const {
workload = null,
milliseconds = 1000,
isAutostart = true,
repeat = 1,
isInterval = false,
subTimers = [],
} = timer;
const newTimer = new Timer(workload, milliseconds, false, repeat, isInterval);
newTimer.isAutostart = isAutostart;
if (Array.isArray(subTimers) === true) {
newTimer.subTimers = Timer.treeWalk(subTimers);
}
timerReferences.push(newTimer);
},
);
return timerReferences;
}
}
Console Output
Promise2 Class (Working Alternative)
class Promise2 {
constructor(executor = null) {
this.promise = new Promise(
(resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
this.return = executor(resolve, reject);
},
);
this.then = function (onfulfilled = null, onrejected = null) {
return this.promise.then(...arguments);
};
this.catch = function (onrejected = null) {
return this.promise.catch(...arguments);
};
this.finally = function (onfinally = null) {
return this.promise.finally(...arguments);
};
}
static create(executor = null) {
return new Promise2(...arguments);
}
}