use idle tasks to do the work

pull/11717/head
Dominic Gannaway 2 years ago
parent 6fa1c06980
commit 648d08f814

@ -7,6 +7,7 @@ import { add_form_reset_listener, autofocus } from './misc.js';
import { effect, effect_root } from '../../reactivity/effects.js';
import * as w from '../../warnings.js';
import { LOADING_ATTR_SYMBOL } from '../../constants.js';
import { queue_idle_task } from '../task.js';
/**
* The value/checked attribute in the template actually corresponds to the defaultValue property, so we need
@ -16,16 +17,22 @@ import { LOADING_ATTR_SYMBOL } from '../../constants.js';
*/
export function remove_input_attr_defaults(dom) {
if (hydrating) {
// @ts-expect-error
dom.__on_r = () => {
// TODO: do we really need to apply this for selects? the value attribute has no effect on the reset behavior
if (dom.tagName === 'INPUT') {
/** @type {HTMLInputElement} */ (dom).defaultValue = '';
/** @type {HTMLInputElement} */ (dom).defaultChecked = false;
} else {
dom.value = '';
}
let already_removed = false;
// We try and remove the default attributes later, rather than sync during hydration.
// Doing it sync during hydration has a negative impact on performance, but deferring the
// work in an idle task alleviates this greatly. If a form reset event comes in before
// the idle callback, then we ensure the input defaults are cleared just before.
const remove_defaults = () => {
if (already_removed) return;
already_removed = true;
const value = dom.getAttribute('value');
set_attribute(dom, 'value', null);
set_attribute(dom, 'checked', null);
if (value) dom.value = value;
};
// @ts-expect-error
dom.__on_r = remove_defaults;
queue_idle_task(remove_defaults);
add_form_reset_listener();
}
}

@ -1,7 +1,7 @@
import { STATE_SYMBOL } from '../../../constants.js';
import { effect, render_effect } from '../../../reactivity/effects.js';
import { untrack } from '../../../runtime.js';
import { queue_task } from '../../task.js';
import { queue_micro_task } from '../../task.js';
/**
* @param {any} bound_value
@ -49,7 +49,7 @@ export function bind_this(element_or_component, update, get_value, get_parts) {
return () => {
// We cannot use effects in the teardown phase, we we use a microtask instead.
queue_task(() => {
queue_micro_task(() => {
if (parts && is_bound_this(get_value(...parts), element_or_component)) {
update(null, ...parts);
}

@ -2,7 +2,7 @@ import { render_effect } from '../../reactivity/effects.js';
import { all_registered_events, root_event_handles } from '../../render.js';
import { define_property, is_array } from '../../utils.js';
import { hydrating } from '../hydration.js';
import { queue_task } from '../task.js';
import { queue_micro_task } from '../task.js';
/**
* SSR adds onload and onerror attributes to catch those events before the hydration.
@ -56,7 +56,7 @@ export function create_event(event_name, dom, handler, options) {
// defer the attachment till after it's been appended to the document. TODO: remove this once Chrome fixes
// this bug.
if (event_name.startsWith('pointer')) {
queue_task(() => {
queue_micro_task(() => {
dom.addEventListener(event_name, target_handler, options);
});
} else {

@ -1,33 +1,63 @@
import { run_all } from '../../shared/utils.js';
let is_task_queued = false;
// Fallback for when requestIdleCallback is not available
const request_idle_callback =
typeof requestIdleCallback === 'undefined'
? (/** @type {() => void} */ cb) => setTimeout(cb, 1)
: requestIdleCallback;
let is_micro_task_queued = false;
let is_idle_task_queued = false;
/** @type {Array<() => void>} */
let current_queued_miro_tasks = [];
/** @type {Array<() => void>} */
let current_queued_tasks = [];
let current_queued_idle_tasks = [];
function process_micro_tasks() {
is_micro_task_queued = false;
const tasks = current_queued_miro_tasks.slice();
current_queued_miro_tasks = [];
run_all(tasks);
}
function process_task() {
is_task_queued = false;
const tasks = current_queued_tasks.slice();
current_queued_tasks = [];
function process_idle_tasks() {
is_idle_task_queued = false;
const tasks = current_queued_idle_tasks.slice();
current_queued_idle_tasks = [];
run_all(tasks);
}
/**
* @param {() => void} fn
*/
export function queue_task(fn) {
if (!is_task_queued) {
is_task_queued = true;
queueMicrotask(process_task);
export function queue_micro_task(fn) {
if (!is_micro_task_queued) {
is_micro_task_queued = true;
queueMicrotask(process_micro_tasks);
}
current_queued_miro_tasks.push(fn);
}
/**
* @param {() => void} fn
*/
export function queue_idle_task(fn) {
if (!is_idle_task_queued) {
is_idle_task_queued = true;
request_idle_callback(process_idle_tasks);
}
current_queued_tasks.push(fn);
current_queued_idle_tasks.push(fn);
}
/**
* Synchronously run any queued tasks.
*/
export function flush_tasks() {
if (is_task_queued) {
process_task();
if (is_micro_task_queued) {
process_micro_tasks();
}
if (is_idle_task_queued) {
process_idle_tasks();
}
}

Loading…
Cancel
Save