forked from nodejs/node
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpromise_hooks.js
126 lines (106 loc) · 3 KB
/
promise_hooks.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
'use strict';
const {
ArrayPrototypeIndexOf,
ArrayPrototypePush,
ArrayPrototypeSlice,
ArrayPrototypeSplice,
FunctionPrototypeBind,
} = primordials;
const { setPromiseHooks } = internalBinding('async_wrap');
const { triggerUncaughtException } = internalBinding('errors');
const { kEmptyObject } = require('internal/util');
const { validatePlainFunction } = require('internal/validators');
const hooks = {
init: [],
before: [],
after: [],
settled: [],
};
function initAll(promise, parent) {
const hookSet = ArrayPrototypeSlice(hooks.init);
const exceptions = [];
for (let i = 0; i < hookSet.length; i++) {
const init = hookSet[i];
try {
init(promise, parent);
} catch (err) {
ArrayPrototypePush(exceptions, err);
}
}
// Triggering exceptions is deferred to allow other hooks to complete
for (let i = 0; i < exceptions.length; i++) {
const err = exceptions[i];
triggerUncaughtException(err, false);
}
}
function makeRunHook(list) {
return (promise) => {
const hookSet = ArrayPrototypeSlice(list);
const exceptions = [];
for (let i = 0; i < hookSet.length; i++) {
const hook = hookSet[i];
try {
hook(promise);
} catch (err) {
ArrayPrototypePush(exceptions, err);
}
}
// Triggering exceptions is deferred to allow other hooks to complete
for (let i = 0; i < exceptions.length; i++) {
const err = exceptions[i];
triggerUncaughtException(err, false);
}
};
}
const beforeAll = makeRunHook(hooks.before);
const afterAll = makeRunHook(hooks.after);
const settledAll = makeRunHook(hooks.settled);
function maybeFastPath(list, runAll) {
return list.length > 1 ? runAll : list[0];
}
function update() {
const init = maybeFastPath(hooks.init, initAll);
const before = maybeFastPath(hooks.before, beforeAll);
const after = maybeFastPath(hooks.after, afterAll);
const settled = maybeFastPath(hooks.settled, settledAll);
setPromiseHooks(init, before, after, settled);
}
function stop(list, hook) {
const index = ArrayPrototypeIndexOf(list, hook);
if (index >= 0) {
ArrayPrototypeSplice(list, index, 1);
update();
}
}
function makeUseHook(name) {
const list = hooks[name];
return (hook) => {
validatePlainFunction(hook, `${name}Hook`);
ArrayPrototypePush(list, hook);
update();
return FunctionPrototypeBind(stop, null, list, hook);
};
}
const onInit = makeUseHook('init');
const onBefore = makeUseHook('before');
const onAfter = makeUseHook('after');
const onSettled = makeUseHook('settled');
function createHook({ init, before, after, settled } = kEmptyObject) {
const hooks = [];
if (init) ArrayPrototypePush(hooks, onInit(init));
if (before) ArrayPrototypePush(hooks, onBefore(before));
if (after) ArrayPrototypePush(hooks, onAfter(after));
if (settled) ArrayPrototypePush(hooks, onSettled(settled));
return () => {
for (const stop of hooks) {
stop();
}
};
}
module.exports = {
createHook,
onInit,
onBefore,
onAfter,
onSettled,
};