JavaScript事件循环

1.线程与进程

进程是 CPU资源分配的最小单位;线程是 CPU调度的最小单位。
一个进程是由多个线程组成

2.多进程与多线程

多进程:在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。
多线程:程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
比如,打开浏览器的一个tab,就是创建了一个进程,这个进程下又有多个线程,比如渲染线程、jS引擎线程、http请求线程等等

3.javascript 中的执行机制

javascript是一门单线程的非阻塞的脚本语言。这是由其最初的用途来决定的:与浏览器交互。
单线程:一个主线程来处理所有的任务,为了防止主线程的阻塞,JavaScript 有了同步和异步的概念。
非阻塞则是当代码需要进行一项异步任务时候,主线程会挂起这个任务,然后在异步任务返回结果的时候再根据一定规则去执行相应的回调。

4.同步与异步

同步任务
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务
不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。比如Ajax、DOM的事件操作、setTimeout、Promise的then方法;

// 同步任务 按照顺序一步一步执行
// 异步任务 放入消息队列中,等待同步任务执行完之后,读取消息队列执行setTimeout是异步任务,所以最后打印
console.log(1);
setTimeout(function() {
    console.log(2);
},0)
console.log(3);
//1 3 2

如何实现的异步?是通过的事件循环(event loop)

  1. 把异步回调函数封装成一个宏任务,添加到消息队列尾部,当循环系统执行到该任务的时候执行回调函数
  2. 执行时机是在主函数执行结束之后、当前宏任务结束之前执行回调函数,这通常都是以微任务形式体现的

5.宏任务与微任务

宏任务
一些异步任务的回调会依次进入macro task queue,等待后续被调用,这些异步任务包括:
script(整体代码)、setTimeout、setInterval、I/O、setImmediate (Node.js)
微任务
另一些异步任务的回调会依次进入micro task queue,等待后续被调用,这些异步任务包括:
Promise.then、 MutaionObserver、process.nextTick (Node.js)

6.事件循环

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)

  1. 通俗来说,在一段代码中,先执行同步代码,遇到宏任务,就将其放置到微任务队列;遇到微任务,就将其放置到微任务队列;
  2. 当所有同步代码执行后,将当前的微任务队列调入主线程执行;
  3. 执行完微任务队列后再读取宏任务队列排在最前的任务,重复1,2步骤
  4. 一直循环直至所有任务执行完毕。

例子1

Promise.resolve().then(()=>{
  console.log('Promise1')  
  setTimeout(()=>{
    console.log('setTimeout2')
  },0)
})
setTimeout(()=>{
  console.log('setTimeout1')
  Promise.resolve().then(()=>{
    console.log('Promise2')    
  })
},0)
console.log("script");
//1 第一次循环,整体script作为第一个宏任务进入主线程,遇到Promise.then(),放入微任务队列;遇到setTimeout,里面的函数放入宏任务队列;遇到console.log,打印script;--script
//2 同步任务执行完毕,查看微任务队列,执行最上面的微任务,打印Promise1;遇到setTimeout同时会生成一个宏任务,里面的函数放入宏任务队列;--Promise1
//3 微任务队列执行完毕,开始第二次循环,去查看宏任务队列,执行最先加入的宏任务,打印setTimeout1,遇到Promise.then(),放入微任务队列;--setTimeout1
//4 清空微任务队列中的所有任务,输出Promise2; --Promise2
//5 微任务队列执行完毕,开始第三次循环,去执行宏任务队列,输出setTimeout2;--setTimeout2

例子2

setTimeout(() => {
  console.log('A');
  Promise.resolve().then(()=>{
    console.log('F')    
  })
}, 0);
var obj = {
  func: function() {
    setTimeout(function() {
      console.log('B');
    }, 0);
    return new Promise(function(resolve) {
      console.log('C');
      resolve();
    });
  },
};
obj.func().then(function() {
  console.log('D');
});
console.log('E');
//第一次循环,整体script作为第一个宏任务,遇到setTimeout,放入宏任务队列;执行obj.func(),遇到setTimeout,放入宏任务队列;
//      new Promise实例是同步任务,打印C,obj.func().then()放入微任务队列;往下执行打印E
//      --C E   宏任务队列[setTimeout1,setTimeout2]微任务队列[obj.func().then()]
//      同步任务执行完毕,查看微任务队列,打印D  --D
//第二次循环,执行下一个宏任务(第2次循环),打印A;遇到Promise.then(),放入微任务队列  
//      --A   宏任务队列[setTimeout2]微任务队列[Promise.resolve().then()]
//      主函数执行完毕,查看微任务队列,打印F  --F
//第三次循环,执行下一个宏任务(第3次循环),打印B  --B

参考
1 浏览器与Node的事件循环(Event Loop)有何区别?
2 最后一次搞懂 Event Loop
3 JavaScript执行机制
4 18 | 宏任务和微任务:不是所有任务都是一个待遇

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值