JacaScript task queues 任务队列

task queues 任务队列

标题非常感谢各位博主的博客,很多都是我复制过来的,抱歉了。
由于看了很多的博客,很多博主没有记住,没有写你们的链接,在此引用了你们的文章,希望谅解。

  • 所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。 (ES6中出现宏微任务)。

    • 同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
    • 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。(每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。)
    • 所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
  • 具体来说,异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。)

    • (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

    • (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

    • (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

    • (4)主线程不断重复上面的第三步。

  • 异步模式"非常重要。在浏览器端,耗时很长的操作都应该异步执行,避免浏览器失去响应,最好的例子就是Ajax操作。在服务器端,"异步模式"甚至是唯一的模式,因为执行环境是单线程的,如果允许同步执行所有http请求,服务器性能会急剧下降,很快就会失去响应.

  • setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

  • HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为10毫秒。另外,对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次。这时使用requestAnimationFrame()的效果要好于setTimeout()。

  • 需要注意的是,setTimeout()只是将事件插入了"任务队列",必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

img-wXZImB75-1575856239968

  • Js 中,异步任务有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。
  • 宏任务队列有多个,之间有优先级,优先级高的优先执行,一次事件循环执行一个任务。所以是一次事件循环中选择其中一个优先级高的队列的一个宏任务执行。微任务队列只有一个,一次事件循环中全部执行完毕。(执行一个宏任务,然后执行该宏任务期间添加的所有微任务,然后执行一次 UI 渲染。然后再执行一个宏任务,以此往复。)

  • microtask:微任务,优先级高,并且可以插队,不是先定义先执行。包括:promise的 .then,Object.observer,MutationObserver,process.nextTick

  • macrotask:宏任务,优先级低,先定义的先执行。包括:script(全局任务), ajax,setTimeout,setInterval,事件绑定,postMessage,MessageChannel(用于消息通讯),setImmediate, I/O, UI rendering.

  • new Promise在实例化的过程中所执行的代码都是同步任务,而then中注册的回调才是异步执行的。

setTimeout(function() {  // 异步任务
      console.log(4);
}, 0);

new Promise(function(reslove) {   // 异步任务
console.log(1); // 宏任务
reslove();
}).then(function(data) {  //微任务
console.log(3);
});

console.log(2); // 同步任务

// 会输出 1 2 3 4 
  • 微任务可以插队,且具有优先级,nextTick>Promise>MutationObserver.

  • 浏览器的 Event Loop 遵循的是 HTML5 标准,而 NodeJs 的 Event Loop 遵循的是 libuv。 区别较大,分开讲。我们上面讲到,当stack空的时候,就会从任务队列中,取任务来执行。浏览器这边,共分3步:取一个宏任务来执行。执行完毕后,下一步。取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。更新UI渲染。Event Loop 会无限循环执行上面3步,这就是Event Loop的主要控制逻辑。其中,第3步(更新UI渲染)会根据浏览器的逻辑,决定要不要马上执行更新。毕竟更新UI成本大,所以,一般都会比较长的时间间隔,执行一次更新。

  • 从执行步骤来看,我们发现微任务,受到了特殊待遇!我们代码开始执行都是从script(全局任务)开始,所以,一旦我们的全局任务(属于宏任务)执行完,就马上执行完整个微任务队列。看个例子:

console.log('script start');

// 微任务
Promise.resolve().then(() => {
    console.log('p 1');
});

// 宏任务
setTimeout(() => {
    console.log('setTimeout');
}, 0);

var s = new Date();
while(new Date() - s < 50); // 阻塞50ms

// 微任务
Promise.resolve().then(() => {
    console.log('p 2');
});

console.log('script ent');


/*** output ***/

// one macro task
script start
script ent

// all micro tasks
p 1
p 2

// one macro task again
setTimeout

上面之所以加50ms的阻塞,是因为 setTimeout 的 delayTime 最少是 4ms. 为了避免认为 setTimeout 是因为4ms的延迟而后面才被执行的,我们加了50ms阻塞。
因为MDN的setTimeout文档中提到HTML规范最低延时为4ms:

  • js 任务队列 :运行机制:
    • 1.将同步任务排队到执行栈中,异步任务排队到事件队列中,先执行同步任务,再执行异步任务。

    • 2.在异步任务开始执行前,会把微任务队列执行清空。

    • 3.执行一个宏任务(栈中没有就从事件队列中获取)

    • 4.执行过程中如果遇到微任务,就将它添加到微任务的任务队列中

    • 5.宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)

    • 6.当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染

    • 7.渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值