写在前面:压力只是暂时的,都会过去,这是我一周以为听到的最顿悟的一句话了吧~
1.引言
js作为单线程的运行机制,必定有自己的运行顺序,在听了一次分享后,也好奇这种运行的机制到底是什么?
js可分为同步任务和异步任务,对于同步的任务,我们当然知道按照顺序进行执行,但是对于异步的操作,会有一个优先级的执行顺序,分别为宏任务和微任务
宏任务(macrotasks)和微任务(microtasks)??包含什么?
macrotasks: setTimeout, setInterval, setImmediate, I/O, UI rendering microtasks: process.nextTick, Promises, Object.observe(废弃), MutationObserver
在js执行时候,一个主线程里面都会有一个事件循环(消息循环|运行循环)和事件队列,存放各种要处理的事件信息,通过这个循环不断处理这些事件信息或消息。
谈到这里,很明显知道,其实出现宏任务和微任务和浏览器以及js的执行机制有很大的关系。了解js的执行机制
2.javascript的执行runtime
这是一张javascript的执行机制图
(1) javaScript Engine,Chrome 的引擎就是 V8
(2) Web APIs,DOM 的操作,AJAX,Timeout 等实际上调用的都是这里提供的
(3)Callback Queue,回调的队列,也就是刚刚所有的 Web APIs 里面的回调函数,实际上都是放在这里排队的
(4) EventLoop,事件循环,也就是刚所说的宏任务和微任务的容器
call Stack
本身就是一个调用栈(就像浏览器中的JavaScript解释器),追踪函数执行流的一种机制,当执行环境调用了多个函数时,通过调用栈,我们可以追踪到哪一个函数在执行,执行的函数体中又调用了哪些函数。
每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。
正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。
当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。
当分配的调用栈空间被占满时,会引发“堆栈溢出”。
是存放执行的重要条件,也是因为只有一个调用栈,所以被称为单线程
callback quene
在js的编译阶段,将一些时间防止在执行队列中
EventLoop 事件循环
一个作用就是将callback quene队列里的执行事件放在在call stack中,执行
3.事件循环
js是单线程的,执行较长的js时候,页面会卡死,无法响应,但是所有的操作都会被记住到另外的队列。比如:点击了一个元素,不会立刻的执行,但是等到js加载完毕后就会执行刚才点击的操作,能够知道有一个队列记录了所有有待执行的操作,这个队列分为微观和宏观。微观会比宏观执行得更快。
称为事件循环的原因大多来源于源码
while (queue.waitForMessage()) { queue.processNextMessage(); }
可以看到入伏哦有消息就会执行,没消息就会继续等待
4.宏任务和微任务
是一种人们定义的执行名字,
如何区分宏任务和微任务呢?划分的标准是什么
- Chromium 自定义消息
- Socket 或者文件等 IO 消息
- UI 相关的消息
- 是个内存回收的清理任务,使用过 Java 的童鞋应该都很熟悉,只是在 JavaScript 这是V8内部调用的
- 就是普通的回调,MutationObserver 也是这一类
- Callable
- 包括 Fullfiled 和 Rejected 也就是 Promise 的完成和失败
- Thenable 对象的处理任务
- 宏任务 Macrotasks 就是参与了事件循环的异步任务。
- 微任务 Microtasks 就是没有参与事件循环的“异步”任务。
-
console.log("开始执行1") console.log(Object.keys({a: 1})); setTimeout(() => { console.log(Object.keys({b: 2})); var promise = new Promise((resolve, reject) => { resolve(1); }); promise.then(res => { //后续增加测试 setTimeout(() => { console.log(Object.keys({e:1})) }, 0); console.log(Object.keys({c: 1})); }); }, 2000); console.log("结束执行2")
微观任务是在当前JS调用执行完了之后立刻执行的,是同步的,在同一个调用栈里,没有多线程异步,如这里包括promise.then在内的setTimeout回调里的代码都是在DOMTimer.Fired执行的,只是说then被放到了当前要执行的整一个异步回调函数的最后面执行。所以setTimeout 0是给主线程的消息循环任务队列添加了一个新的task(回调),而promise.then是在当前task的V8里的microtask插入了一个任务。那么肯定是当前正在执行的task执行完了才执行下一个task.vue的nextTick 也是一个微观任务
let img = new Image(); img.src = 'image01.png?_=' + Date.now(); img.onload = function () { console.log('img ready'); } console.log(Object.keys({e: 1}));