requestAnimationFrame、requestIdleCallback、setTimeout、nextTick

487 阅读4分钟

前言

有时候刷抖音看文章,时不时会碰到下面的一些字样,并且有些看着花里胡哨的,这里收集了他们的一些信息,合在一起记录一下,相信有用到的时候,一看就能分清楚,并分清他们

requestIdleCallback、requestAnimationFrame、setTimeout、setInterval、setImmediate、nextTick

requestAnimationFrame

动画帧回调函数 window.requestAnimationFrame()  方法会告诉浏览器你希望执行一个动画,它要求浏览器在下一次重绘之前,调用用户提供的回调函数,且仅仅回调一次,需要一直调用则需要继续调用 requestAnimationFrame 函数

//可以通过参数避免无限递归
let index = 0
function handler() {
    idx++
    if (idx < 6000) {
        window.requestAnimationFrame(handler);
    }
}

window.requestAnimationFrame(handler);

此方法一般用于做动画,平时操作用的不多,也不会参与后面的其他几个时机对比

requestIdleCallback

空闲调用函数 window.requestIdleCallback()

通过该方法插入一个函数,这个函数将在浏览器空闲时期被调用,这使开发者能够在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。函数一般会按先进先调用的顺序执行,然而,如果回调函数指定了执行超时时间timeout,则有可能为了在超时前执行函数而打乱执行顺序。

const idle = window.requestIdleCallback((deadline) => {
    console.log("requestIdleCallback");
});

//带有 options
const idle = window.requestIdleCallback((deadline) => {
    console.log("requestIdleCallback");
}, {
    timeout: 100,
});

//取消该事件
window.cancelIdleCallback(idle); 

ps:由于是空闲执行,所以其优先级极低,甚至可以认为最低(如果不超时的话),优先级设置还在宏队列之后,但如果宏队列中间有等待时间,其也可能会提前执行的,虽然优先级低,但是如果此时等待时间没到,宏队列是没有实际任务加入的,也就是目前除了微队列、宏队列,只有空闲的这个队列有任务可以执行了

setTimeout、setInterval、setImmediate、nextTick

setTimeout、setInterval

这两个也是我们常用的时间函数了,分别是延迟一定时间调用、保持一定间隔时间调用(不会主动停止,需要自己取消),生成任务也就是很多人所说的宏队列中的任务,优先级比微队列的任务(promise、async)要低

//timeout 延迟500ms后执行,执行完毕后结束,可以提前取消
const timeout = setTimeout(() => {}, 500);
clearTimeout(timeout);

//interval每间隔 500ms 后执行,直到主动取消
const interval = setInterval(() => {}, 500);
clearInterval(interval);

ps:虽然也有使用 setInterval 做动画的,但是由于其特性,会被动暂停,也容易卡事件导致卡顿,可能更适合做一些简单滚屏的操作

setImmediate(nodeJS环境使用)

setImmediate 在IO事件回调之后立即执行的回调函数,其优先级基本在 setTimeout 前后,一般不参与比较

//没有等待函数
setImmediate(() => {
  console.log('This will be executed in the next iteration of the event loop');
});

在浏览器环境中,可以使用window.setTimeout 来实现类似的效果。然而,setImmediate 并不是标准的 Web API,在 nodeJS 环境使用,所以在跨浏览器开发时,使用 setTimeout 来代替 setImmediate

nextTick

浏览器 nextTick nextTick 通常会在当前微任务队列中的其他任务执行完毕后(可以理解为微任务队列的末尾),立即执行回调函数,优先级比较低,和 nodejs 有些不一样(vue 中直接 this.$nextTick,React 需要 从 process 中导出 nextTick,然后使用 nextTick)

nodejs的 process.nextTick Node.js 中的 process.nextTick 具有更高的优先级,会在微任务队列中的其他任务之前执行‌,回调函数会在当前执行栈清空后立即执行,执行优先级非常高,和浏览器的有些不一样

process.nextTick(() => {
    console.log("process.nextTick");
});

requestIdleCallback、setTimeout、nextTick 对比

这边就是用 requestIdleCallback、setTimeout、setImmediate、process.nextTick 他们是哪个做一下简单的对比,分别打印

下面是浏览器的执行环境,可以看出浏览器的 nextTick 执行时机在 promise、setTimeout之间

import {nextTick} from "process"

const func1 = async () => {
    console.log(1)
    await func2()
}

const func2 = async () => {
    console.log(2);
};

const func3 = async () => {
    console.log(3);
};

const main = async () => {
    window.requestIdleCallback((deadline) => {
        console.log("requestIdleCallback");
    });
    setTimeout(() => {
        console.log("setTimeout");
    }, 0);
    nextTick(() => {
        console.log("nextTick");
    });
    await func1()
    await func3()
};

main()

1
2
3
nextTick
setTimeout
requestIdleCallback

下面使用 nodejs 执行环境,发现 process.nextTick 的执行时机已然不同(可以搭配循环测试一下, 这里不作为重点了)

const func1 = async () => {
    console.log(1)
    await func2()
}

const func2 = async () => {
    console.log(2);
};

const func3 = async () => {
    console.log(3);
};

const main = async () => {
    globalThis.requestIdleCallback((deadline) => {
        console.log("requestIdleCallback");
    });
    setTimeout(() => {
        console.log("setTimeout");
    }, 0);
    process.nextTick(() => {
        console.log("process.nextTick");
    });
    await func1()
    await func3()
};

main()

// vscode 的  Code Runner 执行结果
打印结果如下所示
1
2
process.nextTick
3
setTimeout
requestIdleCallback