宏任务,微任务与队列

console.log('script start');

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

Promise.resolve()
  .then(function () {
    console.log('promise1');
  })
  .then(function () {
    console.log('promise2');
  });

console.log('script end');

上面的题的打印顺序应该是什么呢?
在这里插入图片描述为什么会这么打印? 而不是按顺序打印?

首先我先看一下js的事件循环是什么?

首先,js是单线程的,单线程的话就不会想多线程那样出现多个线程冲突等问题,但是单线程会出现----阻塞,就像马路上只有一条路,要是前一个车正好是是手动挡,起步慢,那后面的车不就得等着前一个车,但是js却没有这样的问题,js是非阻塞的,是因为js引进了异步与回调方式解决了这个问题。

// eventLoop是一个用作队列的数组
// (先进,先出)
var eventLoop = [ ];
var event;
// “永远”执行
while (true) {
 	// 一次tick
 	if (eventLoop.length > 0) {
	 	// 拿到队列中的下一个事件
	 	event = eventLoop.shift();
	 	// 现在,执行下一个事件
	 	try {
	 		event();
	 	}
	 	catch (err) {
	 		reportError(err); //异步:现在与将来
	 	}
 	}
}

首先看一段事件循环(EventLoop)的伪代码,事件循环是一直执行的,每一次循环都是一个tick,任务开始执行,同步任务会被推送到主线程上执行,异步任务的回调函数会被推到任务队列中等待,等到主线程的任务执行完之后,执行栈空了,就会执行任务队列中的回调函数,将其推送到执行栈执行,一直这样循环着。

我们再来看一下上面的代码?

promise.then 和 定时器都是异步的任务 ,而且settimeout先被加入到任务队列中去,为什么promise.then会比settimeout要先执行?任务队列中的任务也有执行优先级?

原来任务队列中的任务也有宏任务与微任务之分

宏任务与微任务就是对执行栈任务的分类,宏任务就是由宿主环境提供的任务,比如浏览器,node环境,上面的代码中settimeout就是宏任务,而promise.then就是微任务,主线程进入执行栈就是一个宏任务,开始执行打印了’script start’,执行settimeout这是一个宏任务,放到宏任务队列,到promise.then这是一个微任务,放到微任务队列,执行 ‘script end’,然后到任务队列中去,看看有没有微任务,有的话就执行,执行了promise,then,最后执行下一个宏任务 settimeout
在这里插入图片描述
常见的宏任务与微任务
宏任务:主代码块,setTimeout,setInterval等
微任务:Promise.then,process.nextTick,MutationObserver等

我们在看一段代码

 <div class="outer">
        outer
        <div class="inner">inner</div>
    </div>
		var outer = document.querySelector('.outer');
        var inner = document.querySelector('.inner');


        new MutationObserver(function () {
            console.log('mutate');
        }).observe(outer, {
            attributes: true,
        });


        function onClick() {
            console.log('click');

            setTimeout(function () {
                console.log('timeout');
            }, 0);

            Promise.resolve().then(function () {
                console.log('promise');
            });

            outer.setAttribute('data-random', Math.random());
        }

        
        inner.addEventListener('click', onClick);
        outer.addEventListener('click', onClick);

在这里插入图片描述
我们点击inner的话会打印什么?

1.inner点击 执行点击事件,执行里面的代码 打印了’click’
2.执行到settimeout 将settimeout 的回调放到任务队列中等待
3.执行到promise.then 将它的回调放到微任务队列中等待
4.给outer添加自定义属性,执行MutationObserver,MutationObserver是一个微任务,将它的回调放到微任务队列中,
5.任务执行完了,检查微任务队列执行微任务
6.执行promise.then的回调 打印 ‘promise’
7.执行MutationObserver回调 打印 ‘mutate’
8.事件冒泡 触发父元素的click事件
9.执行点击事件,执行里面的代码 打印了’click’
10.执行到settimeout 将settimeout 的回调放到任务队列中等待
11.执行到promise.then 将它的回调放到微任务队列中等待
12.给outer添加自定义属性,执行MutationObserver,MutationObserver是一个微任务,将它的回调放到微任务队列中
13.任务执行完了,检查微任务队列执行微任务
14.执行promise.then的回调 打印 ‘promise’
15.执行MutationObserver回调 打印 ‘mutate’
16.微任务执行完之后去任务队列中执行inner事件中settimeout的回调 打印’timeout’
17.执行outer事件中settimeout的回调 打印’timeout’

打印结果:
在这里插入图片描述

参考资料:
《你不知道的javascript中卷》
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/
https://zhuanlan.zhihu.com/p/88510041

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值