ie 不执行回调函时_setTimeout和setImmediate到底谁先执行,本文让你彻底理解Event Loop...

本文详细探讨了JavaScript的同步和异步执行,解释了JS如何利用多线程实现异步,包括GUI线程、JS引擎线程、定时器线程和事件触发线程。重点讨论了Event Loop的工作原理,区分了宏任务和微任务,以及它们在浏览器和Node.js中的差异。通过实例分析了`setTimeout`和`setImmediate`的执行顺序,揭示了它们与`process.nextTick()`的关系。文章最后总结了异步执行的核心要点,帮助读者深入理解JavaScript的异步执行机制。
摘要由CSDN通过智能技术生成

笔者以前面试的时候经常遇到写一堆setTimeout,setImmediate来问哪个先执行。本文主要就是来讲这个问题的,但是不是简单的讲讲哪个先,哪个后。笼统的知道setImmediatesetTimeout(fn, 0)先执行是不够的,因为有些情况下setTimeout(fn, 0)是会比setImmediate先执行的。要彻底搞明白这个问题,我们需要系统的学习JS的异步机制和底层原理。本文就会从异步基本概念出发,一直讲到Event Loop的底层原理,让你彻底搞懂setTimeout,setImmediatePromise, process.nextTick谁先谁后这一类问题。

同步和异步

同步异步简单理解就是,同步的代码都是按照书写顺序执行的,异步的代码可能跟书写顺序不一样,写在后面的可能先执行。下面来看个例子:

const syncFunc = () => {
    
  const time = new Date().getTime();
  while(true) {
    if(new Date().getTime() - time > 2000) {
      break;
    }
  }
  console.log(2);
}

console.log(1);
syncFunc();
console.log(3);

上述代码会先打印出1,然后调用syncFuncsyncFunc里面while循环会运行2秒,然后打印出2,最后打印出3。所以这里代码的执行顺序跟我们的书写顺序是一致,他是同步代码:

8b97bb8cdf5b3ff1c578d8fc3e56f26a.png

再来看个异步例子:

const asyncFunc = () => {
    
  setTimeout(() => {
    console.log(2);
  }, 2000);
}

console.log(1);
asyncFunc();
console.log(3);

上述代码的输出是:

1e4a892cdfda741fac7ffc279ee20804.png

可以看到我们中间调用的asyncFunc里面的2却是最后输出的,这是因为setTimeout是一个异步方法。他的作用是设置一个定时器,等定时器时间到了再执行回调里面的代码。所以异步就相当于做一件事,但是并不是马上做,而是你先给别人打了个招呼,说xxx条件满足的时候就干什么什么。就像你晚上睡觉前在手机上设置了一个第二天早上7天的闹钟,就相当于给了手机一个异步事件,触发条件是时间到达早上7点。使用异步的好处是你只需要设置好异步的触发条件就可以去干别的事情了,所以异步不会阻塞主干上事件的执行。特别是对于JS这种只有一个线程的语言,如果都像我们第一个例子那样去while(true),那浏览器就只有一直卡死了,只有等这个循环运行完才会有响应

JS异步是怎么实现的

我们都知道JS是单线程的,那单线程是怎么实现异步的呢?事实上所谓的"JS是单线程的"只是指JS的主运行线程只有一个,而不是整个运行环境都是单线程。JS的运行环境主要是浏览器,以大家都很熟悉的Chrome的内核为例,他不仅是多线程的,而且是多进程的:

202dc4310e40aefe76c382cd4870cec9.png

上图只是一个概括分类,意思是Chrome有这几类的进程和线程,并不是每种只有一个,比如渲染进程就有多个,每个选项卡都有自己的渲染进程。有时候我们使用Chrome会遇到某个选项卡崩溃或者没有响应的情况,这个选项卡对应的渲染进程可能就崩溃了,但是其他选项卡并没有用这个渲染进程,他们有自己的渲染进程,所以其他选项卡并不会受影响。这也是Chrome单个页面崩溃并不会导致浏览器崩溃的原因,而不是像老IE那样,一个页面卡了导致整个浏览器都卡。

对于前端工程师来说,主要关心的还是渲染进程,下面来分别看下里面每个线程是做什么的。

GUI线程

GUI线程就是渲染页面的,他解析HTML和CSS,然后将他们构建成DOM树和渲染树就是这个线程负责的。

JS引擎线程

这个线程就是负责执行JS的主线程,前面说的"JS是单线程的"就是指的这个线程。大名鼎鼎的Chrome V8引擎就是在这个线程运行的。需要注意的是,这个线程跟GUI线程是互斥的。互斥的原因是JS也可以操作DOM,如果JS线程和GUI线程同时操作DOM,结果就混乱了,不知道到底渲染哪个结果。这带来的后果就是如果JS长时间运行,GUI线程就不能执行,整个页面就感觉卡死了。所以我们最开始例子的while(true)这样长时间的同步代码在真正开发时是绝对不允许的

定时器线程

前面异步例子的setTimeout其实就运行在这里,他跟JS主线程根本不在同一个地方,所以“单线程的JS”能够实现异步。JS的定时器方法还有setInterval,也是在这个线程。

事件触发线程

定时器线程其实只是一个计时的作用,他并不会真正执行时间到了的回调,真正执行这个回调的还是JS主线程。所以当时间到了定时器线程会将这个回调事件给到事件触发线程,然后事件触发线程将它加到事件队列里面去。最终JS主线程从事件队列取出这个回调执行。事件触发线程不仅会将定时器事件放入任务队列,其他满足条件的事件也是他负责放进任务队列。

异步HTTP请求线程

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值