Open Samgao0312 opened 2 years ago
“事件循环(Event Loop)机制” == “js 的执行机制问题”
首先,JavaScript是一个单线程的脚本语言。js代码会一行一行的执行,如果碰到定时器,网络请求等这些基于回调函数的任务,假如等待他执行完成以后再进行下一步操作的话,这是不合理的。所以我们有一个专门的 队列 来存放这些任务(如:DOM事件、定时器、网络请求),而这些任务又被称为异步任务。
事件循环(Event Loop)简单来说就是在程序本身运行的主线程会形成一个"执行栈",除此之外,设立一个"任务队列",每当有异步任务完成之后,就会在"任务队列"中放置一个事件,当"执行栈"所有的任务都完成之后,会去"任务队列"中看有没有事件,有的话就放到"执行栈"中执行。
这个过程会不断重复,这种机制就被称为事件循环(Event Loop)机制。而宏任务和微任务靠这个机制来按顺序的执行。
Javascript 的事件分为同步任务 和 异步任务(又分宏任务和微任务),遇到同步任务就放在执行栈中执行,而碰到异步任务就放到 任务队列之中,等到执行栈执行完毕之后,再去执行任务队列之中的事件。
执行栈 是一个存储函数调用的栈结构,遵循先进后出的顺序。
JS引擎常驻于内存中,等待宿主将JS代码或函数传递给它。 也就是等待宿主环境分配宏观任务,反复等待 - 执行即为事件循环。
Event Loop中,每一次循环称为tick,每一次 tick 的任务如下:
定义: 宏任务: 常见的定时器,用户交互事件等等.(宏任务就是特定的这些个任务,没什么特殊含义) 微任务: Promise相关任务,MutationObserver等(一样,只是一种称呼而已!!!) ES6 规范中,microtask 称为 jobs,macrotask 称为 task。 宏任务是由宿主发起的,而微任务由JavaScript自身发起。
定义:
ES6 规范中,microtask 称为 jobs,macrotask 称为 task。 宏任务是由宿主发起的,而微任务由JavaScript自身发起。
宏任务可以被理解为每次"执行栈"中所执行的代码,而浏览器会在每次宏任务执行结束后,在下一个宏任务执行开始前,对页面进行渲染。宏任务包括:
script(整体代码) setTimeout setInterval I/O UI交互事件 Dom渲染 postMessage MessageChannel setImmediate(Node.js 环境) requestAnimationFrame
微任务,可以理解是在当前"执行栈"中的任务执行结束后立即执行的任务。而且早于页面渲染和取任务队列中的任务。微任务包括:
Promise.then await Object.observe MutationObserver process.nextTick(Node.js 环境)
因为当主线程(执行栈)的代码执行完毕之后,在Event Loop执行之前,首先会尝试DOM渲染,这个时候,微任务是在DOM渲染之前执行,DOM渲染完成了之后会执行宏任务,所以,微任务要比宏任务更早执行!!! 那么我们的最终图示如下
JS 代码的执行顺序和规则(Event Loop):
题-1:
console.log('start') setTimeout(() => { console.log('setTimeout') }, 0) new Promise((resolve) => { console.log('promise') resolve() }) .then(() => { console.log('then1') }) .then(() => { console.log('then2') }) console.log('end')
上面代码一次输出:
start promise end then1 then2 setTimeout
题-2:
function app() { setTimeout(() => { console.log("1-1"); Promise.resolve().then(() => { console.log("2-1"); }); }); console.log("1-2"); Promise.resolve().then(() => { console.log("1-3"); setTimeout(() => { console.log("3-1"); }); }); } app(); // 依次输出 1-2 1-3 1-1 2-1 3-1
题-3:
const promise = new Promise((resolve, reject) => { console.log(1); resolve(); console.log(2); }) promise.then(() => { console.log(3); }) console.log(4); // 依次输出: 1 2 4 3
解析:promise是建立后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后才是输出3。
首先,JavaScript是一个单线程的脚本语言。js代码会一行一行的执行,如果碰到定时器,网络请求等这些基于回调函数的任务,假如等待他执行完成以后再进行下一步操作的话,这是不合理的。所以我们有一个专门的 队列 来存放这些任务(如:DOM事件、定时器、网络请求),而这些任务又被称为异步任务。
事件循环(Event Loop)简单来说就是在程序本身运行的主线程会形成一个"执行栈",除此之外,设立一个"任务队列",每当有异步任务完成之后,就会在"任务队列"中放置一个事件,当"执行栈"所有的任务都完成之后,会去"任务队列"中看有没有事件,有的话就放到"执行栈"中执行。
这个过程会不断重复,这种机制就被称为事件循环(Event Loop)机制。而宏任务和微任务靠这个机制来按顺序的执行。
1 事件循环(EventLoop)
Javascript 的事件分为同步任务 和 异步任务(又分宏任务和微任务),遇到同步任务就放在执行栈中执行,而碰到异步任务就放到 任务队列之中,等到执行栈执行完毕之后,再去执行任务队列之中的事件。
Event Loop中,每一次循环称为tick,每一次 tick 的任务如下:
2 宏任务和微任务
3 宏任务有哪些?微任务有哪些?
宏任务可以被理解为每次"执行栈"中所执行的代码,而浏览器会在每次宏任务执行结束后,在下一个宏任务执行开始前,对页面进行渲染。宏任务包括:
微任务,可以理解是在当前"执行栈"中的任务执行结束后立即执行的任务。而且早于页面渲染和取任务队列中的任务。微任务包括:
4、为什么微任务先于宏任务执行?
JS 代码的执行顺序和规则(Event Loop):
5、 思考题
题-1:
上面代码一次输出:
题-2:
题-3:
解析:promise是建立后立即执行,所以会先输出 1,2,而 Promise.then() 内部的代码在 当次 事件循环的 结尾 立刻执行 ,所以会继续输出4,最后才是输出3。
6、参考阅读