浅析Js的宏任务和微任务

  1. 总体概念
  2. Promise
  3. 总结

总体概念

ES6 规范中,microtask(微任务) 称为 jobs,macrotask(宏任务也叫主任务) 称为 task
宏任务是由宿主发起的,而微任务由JavaScript自身发起。

JavaScript 引擎等待宿主环境分配宏观任务,在操作系统中,通常等待的行为都是一个事件循环,所以在 Node 术语中,也会把这个部分称为事件循环

Tips
宏任务其实是最先执行的任务。正确的执行顺序是:先执行一个宏任务《script》标签,script标签包裹着setimout宏任务和promise微任务,先执行script标签内的微任务(promise),进行下一次轮询,执行新的宏任务(setimout)

有了宏观任务和微观任务机制,我们就可以实现 JavaScript 引擎级和宿主级的任务了,例如:Promise 永远在队列尾部添加微任务。setTimeout 等宿主 API,则会添加宏任务。

Promise

Promise 是 JavaScript 语言提供的一种标准化的异步管理方式,它的总体思想是,需要进行 io、等待或者其它异步操作的函数,不返回真实结果,而返回一个“承诺”,函数的调用方可以在合适的时机,选择等待这个承诺兑现(通过 Promise 的 then 方法的回调)。Promise 的基本用法示例如下:

function wait(duration) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve,duration);
    })
}
wait(3000).then( ()=> console.log("ok!"));

这段代码定义了一个函数 wait,它的作用是等候传入参数指定的时长。

Promise 的 then 回调是一个异步的执行过程,下面我们就来研究一下 Promise 函数中的执行顺序,我们来看一段代码示例:

var p = new Promise(function(resolve, reject){
    console.log("a");
    resolve()
});
p.then(() => console.log("c"));
console.log("b")

执行以上代码,输出顺序是a->b->c
在进入 console.log('b') 之前, p 已经得到了 resolve,但是 Promiseresolve 始终是异步操作,所以 c 无法出现在 b 之前。

接下来我们试试跟 setTimeout 混用的 Promise:

var p = new Promise(function(resolve, reject){
    console.log("a");
    resolve()
});
setTimeout(()=>console.log("d"), 0)
p.then(() => console.log("c"));
console.log("b")

运行以上代码,不论顺序如何,d必定发生在c之后,因为 Promise 产生的是 JavaScript 引擎内部的微任务,而 setTimeout 是浏览器 API,它产生宏任务。

再设计一段代码:执行一个耗时1s的Promise

setTimeout(()=>console.log("d"), 0)
var p = new Promise(function(resolve, reject){
    resolve()
});
p.then(() => { 
    var begin = Date.now();
    while(Date.now() - begin < 1000);
    console.log("c1") 
    new Promise(function(resolve, reject){
        resolve()
    }).then(() => console.log("c2"))
});

运行代码可以发现,即使耗时一秒的 c1 执行完毕,再排队的 c2,仍然先于 d 执行了,这很好地解释了微任务优先的原理。

总结一下如何分析异步执行的顺序:

  • 首先我们分析有多少个宏任务;
  • 在每个宏任务中,分析有多少个微任务;
  • 根据调用次序,确定宏任务中的微任务执行次序;
  • 根据宏任务的触发规则和调用次序,确定宏任务的执行次序;
  • 确定整个顺序。

总结

因为JS是单线程语言,只能同时做一件事儿,所以当遇到宏任务,就先执行宏任务,将宏任务放入eventqueue,然后再执行微任务。

js任务需要排队顺序执行,如果一个任务时间过长,后边的任务也会等着,所以这个时候就会想到异步。

Promise是js提供的一个异步管理方式,可以使用ES6新特性async/await,来为for、if等代码结构提供异步的方式。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 chaoyumail@126.com

×

喜欢就点赞,疼爱就打赏