mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
perf(core): speed up promise hook dispatch (#17616)
This commit is contained in:
parent
ed3a7ce2f7
commit
c83abb80fe
2 changed files with 85 additions and 48 deletions
|
@ -326,41 +326,76 @@
|
|||
}
|
||||
const InterruptedPrototype = Interrupted.prototype;
|
||||
|
||||
const promiseHooks = {
|
||||
init: [],
|
||||
before: [],
|
||||
after: [],
|
||||
resolve: [],
|
||||
hasBeenSet: false,
|
||||
};
|
||||
const promiseHooks = [
|
||||
[], // init
|
||||
[], // before
|
||||
[], // after
|
||||
[], // resolve
|
||||
];
|
||||
|
||||
function setPromiseHooks(init, before, after, resolve) {
|
||||
if (init) ArrayPrototypePush(promiseHooks.init, init);
|
||||
if (before) ArrayPrototypePush(promiseHooks.before, before);
|
||||
if (after) ArrayPrototypePush(promiseHooks.after, after);
|
||||
if (resolve) ArrayPrototypePush(promiseHooks.resolve, resolve);
|
||||
|
||||
if (!promiseHooks.hasBeenSet) {
|
||||
promiseHooks.hasBeenSet = true;
|
||||
|
||||
ops.op_set_promise_hooks((promise, parentPromise) => {
|
||||
for (let i = 0; i < promiseHooks.init.length; ++i) {
|
||||
promiseHooks.init[i](promise, parentPromise);
|
||||
}
|
||||
}, (promise) => {
|
||||
for (let i = 0; i < promiseHooks.before.length; ++i) {
|
||||
promiseHooks.before[i](promise);
|
||||
}
|
||||
}, (promise) => {
|
||||
for (let i = 0; i < promiseHooks.after.length; ++i) {
|
||||
promiseHooks.after[i](promise);
|
||||
}
|
||||
}, (promise) => {
|
||||
for (let i = 0; i < promiseHooks.resolve.length; ++i) {
|
||||
promiseHooks.resolve[i](promise);
|
||||
}
|
||||
});
|
||||
const hooks = [init, before, after, resolve];
|
||||
for (let i = 0; i < hooks.length; i++) {
|
||||
const hook = hooks[i];
|
||||
// Skip if no callback was provided for this hook type.
|
||||
if (hook == null) {
|
||||
continue;
|
||||
}
|
||||
// Verify that the type of `hook` is a function.
|
||||
if (typeof hook !== "function") {
|
||||
throw new TypeError(`Expected function at position ${i}`);
|
||||
}
|
||||
// Add the hook to the list.
|
||||
ArrayPrototypePush(promiseHooks[i], hook);
|
||||
}
|
||||
|
||||
const wrappedHooks = ArrayPrototypeMap(promiseHooks, (hooks) => {
|
||||
switch (hooks.length) {
|
||||
case 0:
|
||||
return undefined;
|
||||
case 1:
|
||||
return hooks[0];
|
||||
case 2:
|
||||
return create2xHookWrapper(hooks[0], hooks[1]);
|
||||
case 3:
|
||||
return create3xHookWrapper(hooks[0], hooks[1], hooks[2]);
|
||||
default:
|
||||
return createHookListWrapper(hooks);
|
||||
}
|
||||
|
||||
// The following functions are used to create wrapper functions that call
|
||||
// all the hooks in a list of a certain length. The reason to use a
|
||||
// function that creates a wrapper is to minimize the number of objects
|
||||
// captured in the closure.
|
||||
function create2xHookWrapper(hook1, hook2) {
|
||||
return function (promise, parent) {
|
||||
hook1(promise, parent);
|
||||
hook2(promise, parent);
|
||||
};
|
||||
}
|
||||
function create3xHookWrapper(hook1, hook2, hook3) {
|
||||
return function (promise, parent) {
|
||||
hook1(promise, parent);
|
||||
hook2(promise, parent);
|
||||
hook3(promise, parent);
|
||||
};
|
||||
}
|
||||
function createHookListWrapper(hooks) {
|
||||
return function (promise, parent) {
|
||||
for (let i = 0; i < hooks.length; i++) {
|
||||
const hook = hooks[i];
|
||||
hook(promise, parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ops.op_set_promise_hooks(
|
||||
wrappedHooks[0],
|
||||
wrappedHooks[1],
|
||||
wrappedHooks[2],
|
||||
wrappedHooks[3],
|
||||
);
|
||||
}
|
||||
|
||||
// Extra Deno.core.* exports
|
||||
|
|
|
@ -595,25 +595,27 @@ fn op_get_promise_details<'a>(
|
|||
#[op(v8)]
|
||||
fn op_set_promise_hooks(
|
||||
scope: &mut v8::HandleScope,
|
||||
init_cb: serde_v8::Value,
|
||||
before_cb: serde_v8::Value,
|
||||
after_cb: serde_v8::Value,
|
||||
resolve_cb: serde_v8::Value,
|
||||
init_hook: serde_v8::Value,
|
||||
before_hook: serde_v8::Value,
|
||||
after_hook: serde_v8::Value,
|
||||
resolve_hook: serde_v8::Value,
|
||||
) -> Result<(), Error> {
|
||||
let init_hook_global = to_v8_fn(scope, init_cb)?;
|
||||
let before_hook_global = to_v8_fn(scope, before_cb)?;
|
||||
let after_hook_global = to_v8_fn(scope, after_cb)?;
|
||||
let resolve_hook_global = to_v8_fn(scope, resolve_cb)?;
|
||||
let init_hook = v8::Local::new(scope, init_hook_global);
|
||||
let before_hook = v8::Local::new(scope, before_hook_global);
|
||||
let after_hook = v8::Local::new(scope, after_hook_global);
|
||||
let resolve_hook = v8::Local::new(scope, resolve_hook_global);
|
||||
let v8_fns = [init_hook, before_hook, after_hook, resolve_hook]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter(|(_, hook)| !hook.v8_value.is_undefined())
|
||||
.try_fold([None; 4], |mut v8_fns, (i, hook)| {
|
||||
let v8_fn = v8::Local::<v8::Function>::try_from(hook.v8_value)
|
||||
.map_err(|err| type_error(err.to_string()))?;
|
||||
v8_fns[i] = Some(v8_fn);
|
||||
Ok::<_, Error>(v8_fns)
|
||||
})?;
|
||||
|
||||
scope.get_current_context().set_promise_hooks(
|
||||
Some(init_hook),
|
||||
Some(before_hook),
|
||||
Some(after_hook),
|
||||
Some(resolve_hook),
|
||||
v8_fns[0], // init
|
||||
v8_fns[1], // before
|
||||
v8_fns[2], // after
|
||||
v8_fns[3], // resolve
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
Loading…
Reference in a new issue