1'use strict'; 2 3const { 4 ArrayPrototypeIndexOf, 5 ArrayPrototypeSlice, 6 ArrayPrototypeSplice, 7 ArrayPrototypePush, 8 FunctionPrototypeBind, 9} = primordials; 10 11const { setPromiseHooks } = internalBinding('async_wrap'); 12const { triggerUncaughtException } = internalBinding('errors'); 13 14const { kEmptyObject } = require('internal/util'); 15const { validatePlainFunction } = require('internal/validators'); 16 17const hooks = { 18 init: [], 19 before: [], 20 after: [], 21 settled: [], 22}; 23 24function initAll(promise, parent) { 25 const hookSet = ArrayPrototypeSlice(hooks.init); 26 const exceptions = []; 27 28 for (let i = 0; i < hookSet.length; i++) { 29 const init = hookSet[i]; 30 try { 31 init(promise, parent); 32 } catch (err) { 33 ArrayPrototypePush(exceptions, err); 34 } 35 } 36 37 // Triggering exceptions is deferred to allow other hooks to complete 38 for (let i = 0; i < exceptions.length; i++) { 39 const err = exceptions[i]; 40 triggerUncaughtException(err, false); 41 } 42} 43 44function makeRunHook(list) { 45 return (promise) => { 46 const hookSet = ArrayPrototypeSlice(list); 47 const exceptions = []; 48 49 for (let i = 0; i < hookSet.length; i++) { 50 const hook = hookSet[i]; 51 try { 52 hook(promise); 53 } catch (err) { 54 ArrayPrototypePush(exceptions, err); 55 } 56 } 57 58 // Triggering exceptions is deferred to allow other hooks to complete 59 for (let i = 0; i < exceptions.length; i++) { 60 const err = exceptions[i]; 61 triggerUncaughtException(err, false); 62 } 63 }; 64} 65 66const beforeAll = makeRunHook(hooks.before); 67const afterAll = makeRunHook(hooks.after); 68const settledAll = makeRunHook(hooks.settled); 69 70function maybeFastPath(list, runAll) { 71 return list.length > 1 ? runAll : list[0]; 72} 73 74function update() { 75 const init = maybeFastPath(hooks.init, initAll); 76 const before = maybeFastPath(hooks.before, beforeAll); 77 const after = maybeFastPath(hooks.after, afterAll); 78 const settled = maybeFastPath(hooks.settled, settledAll); 79 setPromiseHooks(init, before, after, settled); 80} 81 82function stop(list, hook) { 83 const index = ArrayPrototypeIndexOf(list, hook); 84 if (index >= 0) { 85 ArrayPrototypeSplice(list, index, 1); 86 update(); 87 } 88} 89 90function makeUseHook(name) { 91 const list = hooks[name]; 92 return (hook) => { 93 validatePlainFunction(hook, `${name}Hook`); 94 ArrayPrototypePush(list, hook); 95 update(); 96 return FunctionPrototypeBind(stop, null, list, hook); 97 }; 98} 99 100const onInit = makeUseHook('init'); 101const onBefore = makeUseHook('before'); 102const onAfter = makeUseHook('after'); 103const onSettled = makeUseHook('settled'); 104 105function createHook({ init, before, after, settled } = kEmptyObject) { 106 const hooks = []; 107 108 if (init) ArrayPrototypePush(hooks, onInit(init)); 109 if (before) ArrayPrototypePush(hooks, onBefore(before)); 110 if (after) ArrayPrototypePush(hooks, onAfter(after)); 111 if (settled) ArrayPrototypePush(hooks, onSettled(settled)); 112 113 return () => { 114 for (const stop of hooks) { 115 stop(); 116 } 117 }; 118} 119 120module.exports = { 121 createHook, 122 onInit, 123 onBefore, 124 onAfter, 125 onSettled, 126}; 127