settimeout函数用法「附:js的settimeout方法」

小伙伴们大家好,今天杨偲眙讲的文章是settimeout函数用法,js的settimeout方法,还有关于队列,任务,定时器这些的精品干货文章,这些其实都是一些必备知识,只是有些时候我们没有遇到就没有去了解而已!

笔者以前面试的时候经常遇到写一堆setTimeout,setImmediate来问哪个先执行。本文主要就是来讲这个问题的,但是不是简单的讲讲哪个先,哪个后。笼统的知道setImmediate比setTimeout(fn, 0)先执行是不够的,因为有些情况下setTimeout(fn, 0)是会比setImmediate先执行的。要彻底搞明白这个问题,我们需要系统的学习JS的异步机制和底层原理。本文就会从异步基本概念出发,一直讲到Event Loop的底层原理,让你彻底搞懂setTimeout,setImmediate,Promise, 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,然后调用syncFunc,syncFunc里面while循环会运行2秒,然后打印出2,最后打印出3。所以这里代码的执行顺序跟我们的书写顺序是一致,他是同步代码:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

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

JS异步是怎么实现的

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

settimeout函数用法,js的settimeout方法,队列,任务,定时器

流程讲解如下:

主线程每次执行时,先看看要执行的是同步任务,还是异步的API同步任务就继续执行,一直执行完遇到异步API就将它交给对应的异步线程,自己继续执行同步任务异步线程执行异步API,执行完后,将异步回调事件放入事件队列上主线程手上的同步任务干完后就来事件队列看看有没有任务主线程发现事件队列有任务,就取出里面的任务执行主线程不断循环上述流程

定时器不准

Event Loop的这个流程里面其实还是隐藏了一些坑的,最典型的问题就是总是先执行同步任务,然后再执行事件队列里面的回调。这个特性就直接影响了定时器的执行,我们想想我们开始那个2秒定时器的执行流程:

主线程执行同步代码遇到setTimeout,将它交给定时器线程定时器线程开始计时,2秒到了通知事件触发线程事件触发线程将定时器回调放入事件队列,异步流程到此结束主线程如果有空,将定时器回调拿出来执行,如果没空这个回调就一直放在队列里。

上述流程我们可以看出,如果主线程长时间被阻塞,定时器回调就没机会执行,即使执行了,那时间也不准了,我们将开头那两个例子结合起来就可以看出这个效果:

const syncFunc = (startTime) => {  const time = new Date().getTime();  while(true) {    if(new Date().getTime() - time > 5000) {      break;    }  }  const offset = new Date().getTime() - startTime;  console.log(`syncFunc run, time offset: ${offset}`);}const asyncFunc = (startTime) => {  setTimeout(() => {    const offset = new Date().getTime() - startTime;    console.log(`asyncFunc run, time offset: ${offset}`);  }, 2000);}const startTime = new Date().getTime();asyncFunc(startTime);syncFunc(startTime);

执行结果如下:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

上图需要注意以下几点:

一个Event Loop可以有一个或多个事件队列,但是只有一个微任务队列。微任务队列全部执行完会重新渲染一次每个宏任务执行完都会重新渲染一次requestAnimationFrame处于渲染阶段,不在微任务队列,也不在宏任务队列

所以想要知道一个异步API在哪个阶段执行,我们得知道他是宏任务还是微任务。

常见宏任务有:

script (可以理解为外层同步代码)setTimeout/setIntervalsetImmediate(Node.js)I/OUI事件postMessage

常见微任务有:

Promiseprocess.nextTick(Node.js)Object.observeMutaionObserver

上面这些事件类型中要注意Promise,他是微任务,也就是说他会在定时器前面运行,我们来看个例子:

console.log('1');setTimeout(() => {  console.log('2');},0);Promise.resolve().then(() => {  console.log('5');})new Promise((resolve) => {  console.log('3');  resolve();}).then(() => {  console.log('4');})

上述代码的输出是1,3,5,4,2。因为:

先输出1,这个没什么说的,同步代码最先执行console.log(‘2’);在setTimeout里面,setTimeout是宏任务,“2”进入宏任务队列console.log(‘5’);在Promise.then里面,进入微任务队列console.log(‘3’);在Promise构造函数的参数里面,这其实是同步代码,直接输出console.log(‘4’);在then里面,他会进入微任务队列,检查事件队列时先执行微任务同步代码运行结果是“1,3”然后检查微任务队列,输出“5,4”最后执行宏任务队列,输出“2”

Node.js的Event Loop

Node.js是运行在服务端的js,虽然他也用到了V8引擎,但是他的服务目的和环境不同,导致了他API与原生JS有些区别,他的Event Loop还要处理一些I/O,比如新的网络连接等,所以与浏览器Event Loop也是不一样的。Node的Event Loop是分阶段的,如下图所示:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

上面的这个流程说简单点就是在一个异步流程里,setImmediate会比定时器先执行,我们写点代码来试试:

console.log('outer');setTimeout(() => {  setTimeout(() => {    console.log('setTimeout');  }, 0);  setImmediate(() => {    console.log('setImmediate');  });}, 0);

上述代码运行如下:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

好像是setTimeout先输出来,我们多运行几次看看:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

我们还是来理一下流程:

我们代码基本都在readFile回调里面,他自己执行时,已经在poll阶段遇到setTimeout(fn, 0),其实是setTimeout(fn, 1),塞入后面的timers阶段遇到setImmediate,塞入后面的check阶段遇到nextTick,立马执行,输出’nextTick 1’到了check阶段,输出’setImmediate’,又遇到个nextTick,立马输出’nextTick 2’到了下个timers阶段,输出’setTimeout’

这种机制其实类似于我们前面讲的微任务,但是并不完全一样,比如同时有nextTick和Promise的时候,肯定是nextTick先执行,原因是nextTick的队列比Promise队列优先级更高。来看个例子:

const promise = Promise.resolve()setImmediate(() => {  console.log('setImmediate');});promise.then(()=>{    console.log('promise')})process.nextTick(()=>{    console.log('nextTick')})

代码运行结果如下:

settimeout函数用法,js的settimeout方法,队列,任务,定时器

总结

本文从异步基本概念出发一直讲到了浏览器和Node.js的Event Loop,现在我们再来总结一下:

  1. JS所谓的“单线程”只是指主线程只有一个,并不是整个运行环境都是单线程
  2. JS的异步靠底层的多线程实现
  3. 不同的异步API对应不同的实现线程
  4. 异步线程与主线程通讯靠的是Event Loop
  5. 异步线程完成任务后将其放入任务队列
  6. 主线程不断轮询任务队列,拿出任务执行
  7. 任务队列有宏任务队列和微任务队列的区别
  8. 微任务队列的优先级更高,所有微任务处理完后才会处理宏任务
  9. Promise是微任务
  10. Node.js的Event Loop跟浏览器的Event Loop不一样,他是分阶段的
  11. setImmediate和setTimeout(fn, 0)哪个回调先执行,需要看他们本身在哪个阶段注册的,如果在定时器回调或者I/O回调里面,setImmediate肯定先执行。如果在最外层或者setImmediate回调里面,哪个先执行取决于当时机器状况。
  12. process.nextTick不在Event Loop的任何阶段,他是一个特殊API,他会立即执行,然后才会继续执行Event Loop

上面就是本文分享的全部内容,希望可以帮助朋友们,看完了,如果你觉得settimeout函数用法「附:js的settimeout方法」还不错的话记得帮忙点个赞哦,欢迎继续浏览本站,学习更多的知识!

本文发布者:万事通,不代表寂寞网立场,转载请注明出处:https://www.jimowang.com/p/17959.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 jimowangmail@126.com 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年11月25日 09:03
下一篇 2022年11月25日 09:05

相关推荐

  • 五红汤产后一个多月可以喝吗_五红汤产后两个月后能喝吗

    各位小伙伴好,今天褚红英为大家带来了一篇关于五红汤产后一个多月可以喝吗,五红汤产后两个月后能喝吗的分享,还有两个,红糖,红枣,红小豆,花生相关的最新干货分享和实用技巧,通过这篇文章,我希望能够为初学者提供一些实用的建议和技巧,让你更好地学习。 五红汤是比较受欢迎的一种滋补汤,它的做法比较简单,吃起来味道很不错,深受人们喜欢。五红汤可以补血养气,滋补身体,适合…

    2023年4月27日
  • 深圳骗子公司 「最新假冒国企深圳27家企业被曝光」

    小伙伴们大家好,这次卫金霞早带大家来分析下关于深圳骗子公司 (假冒国企深圳27家企业被曝光),以及国企,骗子,企业的相关内容干货,其实这个内容对于新手来说还是挺重要的,因为涉及面很大。如果你认真读了,一定会有所收获! 近期,部分中央企业对外公告了一批假冒国企名单,明确有关公司及其下设各级子公司均为假冒国企,与中央企业无任何隶属或股权关系,也不存在任何投资、合…

    2022年9月24日 自媒体
  • 男子没要到3000元生活费纵火烧家 – 视频

    大家好,我是社会评论员何咏宏,用专业的眼光为您评论最新的社会事件。最近有一则男子没要到3000元生活费纵火烧家引发了大家的广泛关注,成为了网络上的焦点话题,现在,就请跟随我一同探究这个事件的具体情况吧! 原标题:男子没要到3000元生活费纵火烧家 8日,成都一小区突发火灾。据悉,此次火灾发生是由于家庭矛盾,47岁的男子向子女索要3000元生活费未果而纵火烧家…

    自媒体 2024年1月11日
  • 躲春是在立春当天躲还是头一天躲 必懂:立春当天为什么要躲春

    很高兴能和大家相聚,今天韩玉琴要给大家带来一篇有关躲春是在立春当天躲还是头一天躲,立春当天为什么要躲春的文章,还有关于太岁,说法,磁场,脏话,习俗等等一系列实用的干货内容,在写这篇文章之前,我经过了很多次的尝试和总结,希望这些经验对你也有帮助。 躲春是一年之中比较重要的一个习俗,它是在每年立春的时候,犯太岁的人就要一个人待在家里面,不能出门,不能见其他人,从…

    2023年5月24日
  • 青蟹放冰箱冷藏会死吗「详细介绍:青蟹放冰箱会冻死吗」

    最近有很多朋友问韩菲冰关于青蟹放冰箱冷藏会死吗,青蟹放冰箱会冻死吗的事情,还有关于冰箱,温度,时间,常温,海蟹这些的内容,其实这篇文章主要还是为新手朋友整理的,总的来说思路还是很重要! 青蟹是生活中比较常见的一种海蟹,它的个头会比较大,肉质饱满,吃起来鲜香可口,很多人都喜欢吃。青蟹的保鲜时间比较短,所以很多人都会将青蟹放冰箱保存,可以冷藏保存,也可以冷冻保存…

    2023年1月10日
  • 国内认可qs还是us「最新usnews世界大学排名官网专业排名」

    很高兴又和大家见面了,今天杨子航来和大家聊一聊国内认可qs还是us,usnews世界大学排名官网专业排名,以及学校,大学排名,世界,专业,学生这些一系列的相关干货内容,希望你们能通过这篇文章很好的掌握其中的技巧,下面我就来为大家全部一一解析! QS这次先导版的榜单今年被国内学生吐槽了个遍。各种的都有,好多都表示QS我真是栓Q了。 也出现了好多不解的地方,比如…

    2023年1月11日 自媒体