js任务队列

setTimeout和任务对列的联系,单线程优点,性能优化;

一、诡异的SETTIMEOUT

setTimeout(code, ms)函数是指在指定的毫秒数(ms)后调用函数或执行代码块,并返回一个整数,后续可以通过clearTimeout(num)传递这个整数来取消该定时器。

setTimeout(function(){
    console.log(1);
}, 0);
console.log(2);

一个很普通的setTimeout定时器,运行上面的代码输出的结果是2、1,但是明明定时器是:在0ms也就是“立即”执行log(1),然后再log(2)啊,结果顺序却是反的,甚至在log(2)之前加上一个耗时的处理也不会影响这个输出结果:

var startTime = +new Date();
setTimeout(function(){
    console.log(1);
}, 0);
while((+new Date() - startTime) < (1000 * 3)){
    // console.log('...');
}
console.log(2)

以上加了一个3秒的while循环阻塞进程,按正常理解log(2)需要在log(1)后3秒才会输出,但输出结果还是跟之前一样,下面逐步分析这种情况的原因。

二、任务队列

JavaScript是单线程的语言,在执行时必须等前面的任务处理完以后才会处理后面的;而且是一个一个连续同步执行的,不象多线程可以同时处理多个任务。JavaScript中的任务分为同步任务和异步任务,同步任务就是主线程上一个个排队执行的任务;相反的异步任务则不进入主线任务而是被加入到“任务队列”中,任务队列的任务只有在主线任务执行完成之后才去处理任务队列中的任务。

这样就可以解释上面的问题了:setTimeout(function(){console.log(1);}, 0);这里的0代表的是0秒后加入到任务队列并没有执行,他要等主线程上面的任务console.log(2)执行完成后才会执行。

setTimeout、setInterval和回调这两种形式的程序会添加到任务队列,示例如下:

function foo(){
    console.log(1);
}
function bar(){
    console.log(2);
}
foo();
bar();
$('#btn').on('click', function(){
    console.log(3);
});
setTimeout(function(){
    console.log(4);
}, 0);

主线程上的任务就是执行foo()、bar()和后面的事件绑定和设定定时器,而任务队列中有一个事件回调任务和一个定时器任务,主线程foo()、bar()执行结束后,就会询问任务队列是否有任务,然后“全部执行”。所以setTimeout(code, ms)表示的是ms后把code任务添加到队列,而不是说ms后就会执行,就算setTimeout时间设置成0,他也要等主线程上的任务完成才会执行,所以setTimeout的执行时间并不是准确的,往往真正执行的时间要比你设定的时间偏大。

### JavaScript 任务队列的工作原理 JavaScript任务队列是基于事件循环(Event Loop)机制运行的。事件循环的核心在于区分宏任务(Macrotask)和微任务(Microtask),并通过特定的执行顺序来管理这些任务。以下是关于任务队列工作原理的详细说明: #### 1. 宏任务与微任务的定义 - **宏任务**:宏任务是相对较大的任务单元,常见的宏任务包括 `setTimeout`、`setInterval`、`setImmediate`(Node.js 环境)、`requestAnimationFrame`、I/O 操作(如文件读取)以及事件回调(如点击事件或鼠标移动事件)。每次从宏任务队列中取出一个任务执行后,会立即检查并执行所有微任务[^2]。 - **微任务**:微任务是优先级更高的异步任务,通常在当前同步代码或宏任务执行完毕后立即执行。常见的微任务包括 `Promise.then`、`MutationObserver`、`queueMicrotask` 和 `process.nextTick`(Node.js 环境专有)。微任务会在每个宏任务之间被清空[^3]。 #### 2. 执行顺序 事件循环的执行顺序遵循以下规则: - 首先执行同步代码中的所有任务。 - 当同步代码执行完毕后,检查并执行微任务队列中的所有任务,直到微任务队列为空。 - 接着从宏任务队列中取出一个任务执行,并再次检查微任务队列,确保所有微任务被执行完毕。 - 这一过程不断重复,直到任务队列为空[^1]。 #### 3. 示例解析 以下是一个典型的代码示例,展示宏任务和微任务的执行顺序: ```javascript console.log("Script start"); setTimeout(() => { console.log("Timeout"); }, 0); Promise.resolve().then(() => { console.log("Promise 1"); }); console.log("Script end"); ``` 执行结果为: ``` Script start Script end Promise 1 Timeout ``` **解释**: - 同步代码首先执行,输出 "Script start" 和 "Script end"。 - `setTimeout` 是宏任务,被放入宏任务队列。 - `Promise.resolve().then` 是微任务,被放入微任务队列。 - 同步代码执行完毕后,立即执行微任务队列中的任务,输出 "Promise 1"。 - 最后从宏任务队列中取出任务执行,输出 "Timeout"。 #### 4. 浏览器与 Node.js 的区别 虽然浏览器和 Node.js 环境都遵循类似的事件循环机制,但在某些细节上存在差异。例如,Node.js 中的 `process.nextTick` 是一种特殊的微任务,其优先级高于其他微任务,而浏览器环境中没有类似的机制[^2]。 ### 结论 理解宏任务与微任务的区别及其执行顺序,是掌握 JavaScript 异步编程的关键。通过事件循环机制,JavaScript 能够高效地处理同步与异步任务,从而实现复杂的交互逻辑[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值