CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PrismarineJS

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.

GitHub Repository: PrismarineJS/mineflayer
Path: blob/master/lib/promise_utils.js
Views: 789
1
function sleep (ms) {
2
return new Promise(resolve => setTimeout(resolve, ms))
3
}
4
5
function createTask () {
6
const task = {
7
done: false
8
}
9
task.promise = new Promise((resolve, reject) => {
10
task.cancel = (err) => {
11
if (!task.done) {
12
task.done = true
13
reject(err)
14
}
15
}
16
task.finish = (result) => {
17
if (!task.done) {
18
task.done = true
19
resolve(result)
20
}
21
}
22
})
23
return task
24
}
25
26
function createDoneTask () {
27
const task = {
28
done: true,
29
promise: Promise.resolve(),
30
cancel: () => {},
31
finish: () => {}
32
}
33
return task
34
}
35
36
/**
37
* Similar to the 'once' function from the 'events' module, but allows you to add a condition for when you want to
38
* actually handle the event, as well as a timeout. The listener is additionally removed if a timeout occurs, instead
39
* of with 'once' where a listener might stay forever if it never triggers.
40
* Note that timeout and checkCondition, both optional, are in the third parameter as an object.
41
* @param emitter - The event emitter to listen to
42
* @param event - The name of the event you want to listen for
43
* @param [timeout=0] - An amount, in milliseconds, for which to wait before considering the promise failed. <0 = none.
44
* @param [checkCondition] - A function which matches the same signature of an event emitter handler, and should return something truthy if you want the event to be handled. If this is not provided, all events are handled.
45
* @returns {Promise} A promise which will either resolve to an *array* of values in the handled event, or will reject on timeout if applicable. This may never resolve if no timeout is set and the event does not fire.
46
*/
47
function onceWithCleanup (emitter, event, { timeout = 0, checkCondition = undefined } = {}) {
48
const task = createTask()
49
50
const onEvent = (...data) => {
51
if (typeof checkCondition === 'function' && !checkCondition(...data)) {
52
return
53
}
54
55
task.finish(data)
56
}
57
58
emitter.addListener(event, onEvent)
59
60
if (typeof timeout === 'number' && timeout > 0) {
61
// For some reason, the call stack gets lost if we don't create the error outside of the .then call
62
const timeoutError = new Error(`Event ${event} did not fire within timeout of ${timeout}ms`)
63
sleep(timeout).then(() => {
64
if (!task.done) {
65
task.cancel(timeoutError)
66
}
67
})
68
}
69
70
task.promise.catch(() => {}).finally(() => emitter.removeListener(event, onEvent))
71
72
return task.promise
73
}
74
75
function once (emitter, event, timeout = 20000) {
76
return onceWithCleanup(emitter, event, { timeout })
77
}
78
79
function withTimeout (promise, timeout) {
80
return Promise.race([
81
promise,
82
sleep(timeout).then(() => {
83
throw new Error('Promise timed out.')
84
})
85
])
86
}
87
88
module.exports = {
89
once,
90
sleep,
91
createTask,
92
createDoneTask,
93
onceWithCleanup,
94
withTimeout
95
}
96
97