Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.javaScript执行机制 #2

Open
include-all opened this issue Jul 11, 2019 · 0 comments
Open

2.javaScript执行机制 #2

include-all opened this issue Jul 11, 2019 · 0 comments

Comments

@include-all
Copy link
Owner

include-all commented Jul 11, 2019

1.javaScript机制

javaScript是单线程语言,所有的类似的“多线程”都是模拟的,并不是真正的多线程

2.event loop

js对于同步的任务和异步的任务有这不同的执行机制
image
image

  • 同步任务会在主线程上执行,被压入栈,异步的进入Event Table并注册函数。
  • 当异步事件执行完成时,Event Table会将这个函数移入Event Queue。
  • 一旦执行栈中的所有同步任务执行完毕,引擎就会读取任务队列,然后将任务队列中的第一个任务压入执行栈中运行。

主线程不断重复第三步,也就是 只要主线程空了,就会去读取任务队列,该过程不断重复,这就是所谓的事件循环。

如下面例子:

console.log('代码执行开始');
$.ajax({
    url: url,
    data: data,
    success:() => {
        console.log('ajax执行成功!');
    }
});
console.log('代码执行结束');
  • 执行console.log('代码执行开始');
  • ajax执行,进入异步处理的模块,注册回调函数
  • 执行console.log('代码执行结束');
  • 主线程为空,读取读取事件队列,ajax未执行完毕,任务队列为空
  • ajax执行完毕,回调函数加入任务队列
  • 主线程为空,读取读取任务队列,读取到回调函数,将其加入主线程执行

对于setTimeout(() => {task()}, 0)也并不是立即执行,而是需要等到主线程空闲才会执行,event table会立刻处理好,被放入任务队列,等待主线程的读取

ajax请求,会在请求结束后再把回调函数放入宏任务队列;
setTimeout会在时间结束后加入宏任务队列;
这是一个很关键的理解,因为像请求或者计时,这是另一个线程去做的,而结束之后,才会把回调函数放进宏任务队列,有js引擎去执行,所以请求和计时不会阻塞js,但回调是可能被其他代码阻塞的,这一点很关键

从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理

3.宏任务和微任务的理解

1. 宏任务:当前调用栈中执行的代码成为宏任务。(主代码快,定时器等等)。
2. 微任务: 当前(此次事件循环中)宏任务执行完,在下一个宏任务开始之前需要执行的任务,可以理解为回调事件。(promise.then,proness.nextTick等等)。
3. 宏任务中的事件放在callback queue中,由事件触发线程维护;微任务的事件放在微任务队列中,由js引擎线程维护。

image

运行机制:
1. 在执行栈中执行一个宏任务。
2. 执行过程中遇到微任务,将微任务添加到微任务队列中。
3. 当前宏任务执行完毕,立即执行微任务队列中的任务。
4. 当前微任务队列中的任务执行完毕,检查渲染,GUI线程接管渲染。
5. 渲染完毕后,js线程接管,开启下一次事件循环,执行下一次宏任务(事件队列中取)。

常见的宏任务和微任务

宏任务:script(整体代码)、setTimeout、setInterval、I/O、事件、postMessage、 MessageChannel、setImmediate (Node.js)
微任务:Promise.then、 MutaionObserver、process.nextTick (Node.js)

实例详解:

console.log('start');
setTimeout(() => {
    console.log('timeout1')
    Promise.resolve().then(() => {
        console.log('promise1')
    });
    Promise.resolve().then(() => {
        console.log('promise2')
    })
}, 100);
console.log('mid1');
new Promise(function(resolve) {
    console.log('promise3');
    resolve();
}).then(function() {
    console.log('promise4')
});
console.log('mid2');
setTimeout(() => {
    console.log('timeout2');
    Promise.resolve().then(() => {
        console.log('promise5')
    })
}, 200);
console.log('end');
/**第一轮事件循环 event loop开始
 *      整体script作为第一个宏任务,进入执行栈
 *      遇到console.log('start');打印出start
 *      遇到setTimeout,其回调函数被分发到宏任务event queue中,记为setTimeout1
 *      遇到console.log('mid1');打印出mid1
 *      遇到promise,new Promise立即执行,打印出promise3,then被分发到微任务,记为then1
 *      遇到console.log('mid2');打印出mid2
 *      遇到第二个setTimeout,其回调函数被分发到宏任务event queue中,记为setTimeout2
 *      遇到console.log('end');打印出end
 *      此时宏任务event queue中有setTimeout1 setTimeout2,微任务event queue中有then1
 *      ~有微任务,执行微任务then1,打印出promise4
 *   第一轮事件循环结束
 * 第二轮事件循环开始
 *      从宏任务队列取出setTimeout1,进入执行栈执行,打印出timeout1,将两个promise分发到微任务
 *      event loop,记为then2,then3
 *      此时第二轮事件循环的宏任务结束,检查微任务,发现then2,then3,依次执行,
 *      打印出promise1,promise2
 *   第二轮事件循环结束
 * 第三轮事件循环开始
 *      从宏任务队列取出setTimeout2,进入执行栈执行,打印出timeout2,将promise分发到微任务
 *      event loop,记为then4
 *      此时第三轮事件循环的宏任务结束,检查微任务,发现then4,执行,打印出promise5
 *   第三轮事件循环结束
 * 全部程序结束
 * 所以打印顺序如下:start mid1 promise3 mid2 end promise4 timeout1 promise1 promise2
 *                 timeout2 promise5
 * */

总结:

  • javaScript是单线程语言
  • Event Loop是javaScript的执行机制

参考:

hey,你的Event Loop
最后一次搞懂 Event Loop
这一次,彻底弄懂 JavaScript 执行机制

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant