-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
135 lines (106 loc) · 4.21 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
'use strict';
const get = require('lodash/get');
/**
* handle sleep in javascript using promises. Also add a few options to make this more useful
* @param ms
* @param options
* @returns {{valid: boolean, timer: null, tickCount: (function(): number), cancel_timer: cancel_timer, id: *, tick: (function(): *), toObject: (function(): {valid: boolean, timer: null, registry: {valid: boolean, timers: [], tickCount: number}, tickCount: number, ms: *, message, hash: *})}}
*/
const sleeper = module.exports.sleeper = (ms, options) => {
const message = get(options, 'message');
const debug = get(options, 'debug', false);
debug && console.log("sleeper.initialized");
// Track history and current timeout values.
const registry = {valid: true, tickCount: 0, timers: [], current_timer: {timer: null, cancel: null}};
const tickCount = () => {
const tickCount = get(registry, 'tickCount', 0);
return tickCount;
};
const toObject = () => ({
ms,
message,
timer: registry.timer,
registry,
valid: registry.valid,
tickCount: tickCount(),
});
return {
// property values
valid: registry.valid,
timer: registry.timer,
tickCount,
toObject,
// functions
cancel_timer: async () => {
debug && console.log(`canceling.${registry.timer}`);
clearTimeout(registry.timer);
registry.timer = null;
registry.valid = false;
registry.current_timer && await registry.current_timer.cancel(registry);
},
tick: () => {
const new_count = tickCount() + 1;
registry.current_timer.promise = new Promise(resolve => {
registry.current_timer.cancel = resolve;
registry.timer = setTimeout(() => {
debug && console.log(`resolving.tick.${registry.timer}`);
resolve({message, ...toObject()});
registry.tickCount = new_count;
}, ms);
debug && console.log(`starting.tick.${registry.timer}`);
registry.timers.push(registry.timer);
});
return registry.current_timer.promise;
},
};
};
function LooperConstructor() {
// Defaults
this.timer = null;
this.tick = {valid: true};
this.ms = 5000;
this.debug = false;
/**
* cancel the loop in a safe way
* @param options
*/
this.loopCancel = async (options) => {
const debug = get(options, 'debug', false);
debug && console.log("looper.cancel");
this.tick = {valid: false};
this.timer && await this.timer.cancel_timer({debug});
};
/**
*
* @returns {Promise<void>}
* @param options
*/
this.loopStart = async (options) => {
// callback to fire after each tick
const loop_tick_callback = get(options, 'loop_tick_callback');
// timeout between loop ticks
const ms = get(options, 'ms', this.ms);
// Debug information on each tick to the console
const debug = get(options, 'debug', this.debug);
debug && console.log('loopStart.called', {loop_tick_callback, ms, debug});
this.timer = sleeper(ms, {identifier: 'looper@loop-start', debug: debug});
this.tick = {valid: true};
while (this.tick.valid) {
// Await here as a workaround to sleep in js for a new seconds.
// use this to avoid using recursion
this.tick = await this.timer.tick();
if (this.tick.valid) {
debug && console.log('loopStart.tick.done', this.tick, this.tick.valid ? 'is valid' : 'is not valid');
// only show the loading state on the first loop iteration. pass the tick object into the callback function
const response = await loop_tick_callback(this.tick);
// The last callback returned false. This means that the looping should cancel.
if (response === false) {
this.loopCancel({debug});
}
} else {
this.loopCancel({debug});
}
}
}
}
module.exports.looper = new LooperConstructor();