for循环延时_前端中的事件循环eventloop机制

我们知道 js 是单线程执行的,那么异步的代码 js 是怎么处理的呢?例如下面的代码是如何进行输出的:

console.log(1);setTimeout(function() {    console.log(2);}, 0);new Promise(function(resolve) {    console.log(3);    resolve(Date.now());}).then(function() {    console.log(4);});console.log(5);setTimeout(function() {    new Promise(function(resolve) {        console.log(6);        resolve(Date.now());    }).then(function() {        console.log(7);    });}, 0);

在不运行的情况可以先猜测下最终的输出,然后展开我们要说的内容。

1. 宏任务与微任务

依据我们多年编写 ajax 的经验:js 应该是按照语句先后顺序执行,在出现异步时,则发起异步请求后,接着往下执行,待异步结果返回后再接着执行。但他内部是怎样管理这些执行任务的呢?

在 js 中,任务分为宏任务(macrotask)和微任务(microtask),这两个任务分别维护一个队列,均采用先进先出的策略进行执行!同步执行的任务都在宏任务上执行。

宏任务主要有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。

微任务主要有:Promise.then、 MutationObserver、 process.nextTick(Node.js 环境)。

具体的操作步骤如下:

  1. 从宏任务的头部取出一个任务执行;
  2. 执行过程中若遇到微任务则将其添加到微任务的队列中;
  3. 宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则挨个儿出去执行,直到执行完毕;
  4. GUI 渲染;
  5. 回到步骤 1,直到宏任务执行完毕;

这 4 步构成了一个事件的循环检测机制,即我们所称的eventloop。

回到我们上面说的代码:

console.log(1);setTimeout(function() {    console.log(2);}, 0);new Promise(function(resolve) {    console.log(3);    resolve(Date.now());}).then(function() {    console.log(4);});console.log(5);setTimeout(function() {    new Promise(function(resolve) {        console.log(6);        resolve(Date.now());    }).then(function() {        console.log(7);    });}, 0);

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。

执行步骤如下:

  1. 执行 log(1),输出 1;
  2. 遇到 setTimeout,将回调的代码 log(2)添加到宏任务中等待执行;
  3. 执行 console.log(3),将 then 中的 log(4)添加到微任务中;
  4. 执行 log(5),输出 5;
  5. 遇到 setTimeout,将回调的代码 log(6, 7)添加到宏任务中;
  6. 宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,存在一个微任务 log(4)(在步骤 3 中添加的),执行输出 4;
  7. 取出下一个宏任务 log(2)执行,输出 2;
  8. 宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,不存在;
  9. 取出下一个宏任务执行,执行 log(6),将 then 中的 log(7)添加到微任务中;
  10. 宏任务执行完毕,存在一个微任务 log(7)(在步骤 9 中添加的),执行输出 7;

因此,最终的输出顺序为:1, 3, 5, 4, 2, 6, 7;

我们在Promise.then实现一个稍微耗时的操作,这个步骤看起来会更加地明显:

console.log(1);var start = Date.now();setTimeout(function() {    console.log(2);}, 0);setTimeout(function() {    console.log(4, Date.now() - start);}, 400);Promise.resolve().then(function() {    var sum = function(a, b) {        return Number(a) + Number(b);    }    var res = [];    for(var i=0; i<5000000; i++) {        var a = Math.floor(Math.random()*100);        var b = Math.floor(Math.random()*200);        res.push(sum(a, b));    }    res = res.sort();    console.log(3);})

Promise.then中,先生成一个500万随机数的数组,然后对这个数组进行排序。运行这段代码可以发现:马上会输出1,稍等一会儿才会输出3,然后再输出2。不论等待多长时间输出3,2一定会在3的后面输出。这也就印证了eventloop中的第3步操作,必须等所有的微任务执行完毕后,才开始下一个宏任务。

同时,这段代码的输出很有意思:

setTimeout(function() {    console.log(4, Date.now() - start); // 4, 1380 电脑状态的不同,输出的时间差也不一样}, 400);

本来要设定的是400ms后输出,但因为之前的任务耗时严重,导致之后的任务只能延迟往后排。也能说明,setTimeout和setInterval这种操作的延时是不准确的,这两个方法只能大概将任务400ms之后的宏任务中,但具体的执行时间,还是要看线程是否空闲。若前一个任务中有耗时的操作,或者有无限的微任务加入进来时,则会阻塞下一个任务的执行

2. async-await

从上面的代码中也能看到 Promise.then 中的代码是属于微服务,那么 async-await 的代码怎么执行呢?比如下面的代码:

function A() {    return Promise.resolve(Date.now());}async function B() {    console.log(Math.random());    let now = await A();    console.log(now);}console.log(1);B();console.log(2);

其实,async-await 只是 Promise+generator 的一种语法糖而已。上面的代码我们改写为这样,可以更加清晰一点:

function B() {    console.log(Math.random());    A().then(function(now) {        console.log(now);    })}console.log(1);B();console.log(2);

这样我们就能明白输出的先后顺序了: 1, 0.4793526730678652(随机数), 2, 1557830834679(时间戳);

3. requestAnimationFrame

requestAnimationFrame也属于异步执行的方法,但该方法既不属于宏任务,也不属于微任务。按照MDN中的定义:

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

requestAnimationFrame是GUI渲染之前执行,但在微服务之后,不过requestAnimationFrame不一定会在当前帧必须执行,由浏览器根据当前的策略自行决定在哪一帧执行。

4. 总结

我们要记住最重要的两点:js是单线程和eventloop的循环机制。

8796cd89117acdf688914f9e3f197cca.png


作者:腾讯IVWEB团队
链接:https://juejin.im/post/5e0adffbe51d4541013f0bf4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/292979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

由于未分配内存的指针导致段错误

代码如下 #include<iostream> using namespace std;void f(char **s) {*s "chenyu"; }typedef struct _node {int val;struct _node *next; } Node;typedef struct _link {Node *front;Node *rear; } Link;void queue(Link *link) { // link.front link.rear…

androidActivity生命周期

Activity生命周期Activity是一个用来提供用户交互界面的组件&#xff0c;它是四大组件之一&#xff0c;对于我们刚刚学习android的菜鸟来说是非常重要的&#xff0c;我们可以将一个屏幕理解为一个Activity&#xff0c;Activity通常是一个全屏的界面&#xff0c;每一个应用程序可…

Autofac实现有条件的DI

Autofac.Annotation框架是我用.netcore写的一个DI框架&#xff0c;基于Autofac参考 Spring注解方式所有容器的注册和装配,切面,拦截器等都是依赖标签来完成。开源地址&#xff1a;https://github.com/yuzd/Autofac.Annotation本期讲的是最新实现的功能有条件的DI有些时候我们想…

Spark ML - 协同过滤

http://ihoge.cn/2018/ML1.html 协同过滤算法 ​ 获取spark自带的MovieLens数据集&#xff0c;其中每行包含一个用户、一个电影、一个该用户对该电影的评分以及时间戳。我们使用默认的ALS.train() 方法&#xff0c;即显性反馈&#xff08;默认implicitPrefs 为false&#xff…

转:java的各个拓展类库的推荐方案

from: 链接&#xff1a;https://www.zhihu.com/question/21142149/answer/109854408 Java是世界上最强大的编程语言之一&#xff0c;很多开发人员和大型企业都偏爱Java&#xff0c;并且在各种应用场景中使用它。在本文中&#xff0c;我们为大家介绍几种Java库来帮助开发人员解决…

字符串常量到底存放在哪个存储区

字符串常量&#xff0c;放在哪个存储区呢&#xff1f;是“自动存储区”还是“静态存储区”中&#xff1f; 比如&#xff1a; char *pstr"hello world!"; 这里&#xff0c;"hello world!"是一个字符串常量&#xff0c; pstr是在栈中的变量。 我想问&#…

公众平台关注用户达到5万即可开通流量主功能 可以推广APP应用

今天微信公众平台发布发布了一些更新&#xff0c;公众帐号的关注用户达到5万&#xff0c;即可开通流量主功能&#xff0c;之前的是要求10万粉丝&#xff0c;这是一个微信开放的信号。广告主可推广苹果商店应用或腾讯开放平台应用。新增卡片和图文广告规格。以下是微信团队的公告…

二进制全排列 java_排列组合算法真厉害,傻瓜都能学会

作者&#xff1a;枕边书来源&#xff1a;https://zhenbianshu.github.io/2019/01/charming_alg_permutation_and_combination.html需求最近工作中碰到一个需求&#xff1a;我们的数据表有多个维度&#xff0c;任意多个维度组合后进行 group by 可能会产生一些”奇妙”的反应&am…

linux运维常用命令

1.删除0字节文件find -type f -size 0 -exec rm -rf {} \;2.查看进程按内存从大到小排列ps -e -o "%C : %p : %z : %a"|sort -k5 -nr3.按cpu利用率从大到小排列ps -e -o "%C : %p : %z : %a"|sort -nr4.打印说cache里的URLgrep -r -a jpg /dat…

如何计算 string 的 crc32 值 ?

咨询区 Nick Berardi请问我如何通过 C# 计算 string 的 crc32 校验和&#xff1f;回答区 Pete我一直震撼的一件事就是 .NET 底层框架中居然没有提供对 CRC32 的直接支持&#xff0c;CRC32 一直都是 ZIP,RAR 压缩中做校验和的绝佳方案&#xff0c;在 .NET 中你可以这么使用。var…

Spark ML - 聚类算法

http://ihoge.cn/2018/ML2.html Spark ML - 聚类算法 1.KMeans快速聚类 首先到UR需要的包&#xff1a; import org.apache.spark.ml.clustering.{KMeans,KMeansModel} import org.apache.spark.ml.linalg.Vectors 开启RDD的隐式转换&#xff1a; import spark.implicits.…

twitter storm源码走读(五)

TridentTopology创建过程详解 从用户层面来看TridentTopology&#xff0c;有两个重要的概念一是Stream,另一个是作用于Stream上的各种Operation。在实现层面来看&#xff0c;无论是stream&#xff0c;还是后续的operation都会转变成为各个Node&#xff0c;这些Node之间的关系通…

C语言宏使用常见问题

代码&#xff1a; #include<stdio.h> #define MAX(a,b) a>b?a:b #define MIN(a,b) a>b?b:a //#define M (xY) #define M1(m) m*m #define M2(m) (m)*(m) #define M3(m) ((m)*(m)) int main(){int x,y,max,min;printf("Input one numbers:");int sum, m…

ad域管理与维护_AD域管理员账号下发

大家好&#xff0c;最近比较忙&#xff0c;好久没发文章了&#xff0c;这次继续讲AD域的相关内容。AD域运行在Windows Server服务器&#xff0c;用于集中管理网内的所有Windows客户端主机&#xff0c;其中最重要的管理手段便是「域组策略」&#xff0c;可管理的条目非常多&…

[Unity 游戏设计的元素]

1.核心游戏机制 2.主题 3.功能集合 4.可能的附加功能 5.备用主题创意转载于:https://www.cnblogs.com/rayshen/p/3968294.html

路遥工具箱全面迁移至 .NET 6.0 并发布 3.0 版本及迁移记录详解

随着 .NET 6.0 的发布 NewLife 组件也进行了升级&#xff0c;并在 2022 年 1 月份全面停止了对 .NET Framework 的支持框架依赖升级至 .NET Standard 2.1 。2022 年新年到来之际&#xff0c;笔者也打算将路遥工具箱全面迁移至 .NET 6.0 同时发布 3.0 版本。这意味着路遥工具箱将…

java中main函数解析

作者&#xff1a;xwdreamer出处&#xff1a;http://www.cnblogs.com/xwdreamer欢迎任何形式的转载&#xff0c;但请务必注明出处。从写java至今&#xff0c;写的最多的可能就是主函数 public static void main(String[] args) {} 但是以前一直都没有问自己&#xff0c;为什么要…