事件轮询机制 Event Loop、浏览器更新渲染时机、setTimeout VS setInterval

news2024/10/7 8:32:20

目录

1. 事件轮询机制(Event Loop)是什么

1.1 宏任务、微任务

1.2 Event Loop 循环过程

1.3 经典题目分析

1.3.1 第一轮事件循环

1.3.2 第二、三次事件循环

1.3.3 参考文章

2. async、await 在事件轮询中的执行时机

3. 浏览器更新渲染时机、Vue nextTick

3.1 Event Loop 与 浏览器更新渲染时机

3.2 Vue 为什么要优先使用微任务实现 nextTick

4. Node.js 中的 process.nextTick

4.1 什么是 process.nextTick

4.2 浏览器事件循环 VS  Node 事件循环

5. setTimeout VS setInterval

5.1 setTimeout / setInterval 的执行时间为啥无法确定呢?

5.2 setTimeout / setInterval 概念容易踩得坑

5.2.1 setTimeout 举个栗子~

5.2.2 setInterval 举个栗子~

5.2.3 setTimeout 模拟实现 setInterval

5.2.4 setInterval 模拟实现 setTimeout

5.3 requestAnimationFrame 浏览器提供的动画 API

5.3.1 setTimeout / setInterval 动画卡顿

5.3.2 什么是 requestAnimationFrame

5.3.3 setTimeout VS setInterval VS requestAnimationFrame

5.3.4 参考文章


HTML5 中的 WebWoker,号称 JavaScript 多线程版

其实本质上还是用 JavaScript 单线程模拟出来的,所以搞明白 Event Loop 很重要

 

1. 事件轮询机制(Event Loop)是什么

1.1 宏任务、微任务

任务可以分成两种:

  • 宏任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行下一个任务
  • 微任务:不进入主线程、而进入"微任务列表" 的任务

宏任务(Macrotasks)包括:

  • script 全部代码(注意:同步代码也属于宏任务)
  • setTimeout
  • setInterval
  • setImmediate

微任务(Microtasks)包括:

  • Promise
  • MutationObserver

不同类型的任务,会进入对应的 Event Queue,比如 setTimeout 和 setInterval 会进入相同的 Event Queue

1.2 Event Loop 循环过程

JavaScript 是单线程的,同一时间只能做一件事。所有任务都需要排队,前一个任务结束,才会执行后一个任务(JavaScript 是一门单线程语言,Event Loop 是 JavaScript 的执行机制)

  • 宏任务是一个个执行;
  • 如果执行过程中,遇到微任务,就把他加入微任务队列;
  • 当前宏任务执行完后,会判断 微任务列表 中是否有任务;
  • 如果有,就把该微任务放到主线程中执行;如果没有,就继续执行下一个宏任务,不断循环;

用 Ajax 举个栗子:

  • Ajax 作为微任务,进入微任务列表,注册回调函数 success
  • 执行同步代码,console.log('代码执行结束')
  • 等待 Ajax 请求完成,主线程从 微任务列表 读取回调函数 success 并执行
let data = [];
$.ajax({
    url:www.javascript.com,
    data:data,
    success:() => {
        console.log('发送成功!');
    }
})
console.log('代码执行结束');

1.3 经典题目分析

Promise.resolve()
  .then(function() {
    console.log("promise0"); // 2 执行微任务1 Promise1
  })
  .then(function() {
    console.log("promise5"); // 4 执行微任务3 Promise3
  });

setTimeout(() => {
  console.log("timer1"); // 5 执行宏任务 setTimeout1
  Promise.resolve().then(function() {
    console.log("promise2"); // 6 执行宏任务中的微任务
  });
  Promise.resolve().then(function() {
    console.log("promise4"); // 7 执行宏任务中的微任务
  });
}, 0);

setTimeout(() => {
  console.log("timer2"); // 8 执行宏任务 setTimeout2
  Promise.resolve().then(function() {
    console.log("promise3"); // 9 执行宏任务中的微任务
  });
}, 0);

Promise.resolve().then(function() {
  console.log("promise1"); // 3 执行微任务2 Promise2
});

console.log("start"); // 1 执行同步代码

打印结果: start ------ promise0 ------ promise1 ------ promise5 ------ timer1 ------ promise2 ------ promise4 ------ timer2 ------ promise3

 

1.3.1 第一轮事件循环

  • 整体 script 作为第一个宏任务进入主线程,遇到 console.log,输出 start
  • 遇到 Promise,其回调函数 then 被分发到 微任务队列 中。记为 Promise1
  • 遇到 Promise,其回调函数 then 被分发到 微任务队列 中。记为 Promise2
  • 遇到 setTimeout,分发到 宏任务队列。记为 setTimeout1
  • 遇到 setTimeout,分发到 宏任务队列。记为 setTimeout2
  • 遇到 Promise,其回调函数 then 被分发到 微任务队列 中。记为 Promise3

宏任务队列:

  • setTimeout1
  • setTimeout2

微任务队列:

  • Promise1
  • Promise2
  • Promise3

执行同步代码,输出 start

执行微任务队列,输出 promise0 ------ promise1 ------ promise5

第 2、3、4 步的输出可能会有疑问

这里举个栗子~

new Promise(function(resolve) {
    // 这里作为同步代码,立即执行
    console.log('promise');
}).then(function() {
    // then 中的代码,会被放入微任务队列
    console.log('then');
})

可以看出:

  • new Promise 中的代码,是立即执行的
  • 而回调函数 then,会被放到微任务队列中

1.3.2 第二、三次事件循环

第二次事件循环:

  • 执行第二个宏任务 setTimeout1
  • 先执行同步代码,输出 timer1
  • 再将微任务放到微任务列表中,由于主线程没有代码可执行,所以将微任务放到主任务线程中,输出 promise2 ------ promise4

第三次事件循环:

  • 执行第三个宏任务 setTimeout2
  • 先执行同步代码,输出 timer2
  • 再将微任务放到微任务列表中,由于主线程没有代码可执行,所以将微任务放到主任务线程中,输出 promise3

1.3.3 参考文章

这篇文章的示例讲解比较清楚,值得学习

这一次,彻底弄懂 JavaScript 执行机制 - 掘金本文的目的就是要保证你彻底弄懂javascript的执行机制,如果读完本文还不懂,可以揍我。 不论你是javascript新手还是老鸟,不论是面试求职,还是日常开发工作,我们经常会遇到这样的情况:给定的几行代码,我们需要知道其输出内容和顺序。因为javascript是一门单线程…https://juejin.cn/post/6844903512845860872

2. async、await 在事件轮询中的执行时机

async 隐式返回 Promise,会产生一个微任务

await 后面的代码,在执行微任务时执行

举个栗子 ~~ 看下面的代码

console.log("script start"); // 1 同步代码

async function async1() {
  await async2(); // 2 同步代码
  console.log("async1 end"); // 5 这里的执行时机:在执行微任务时执行
}

async function async2() {
  console.log("async2 end");
}

async1();

setTimeout(function() {
  console.log("setTimeout"); // 8 第二个宏任务
}, 0);

new Promise(resolve => {
  console.log("Promise"); // 3 同步代码
  resolve();
})
  .then(function() {
    console.log("promise1"); // 6 微任务
  })
  .then(function() {
    console.log("promise2"); // 7 微任务
  }); 

console.log("script end"); // 4 同步代码

打印结果: script start ------ async2 end ------ Promise ------ script end ------ async1 end ------ promise1 ------ promise2 ------ setTimeout

第一次事件循环:

  • 整体 script 作为第一个宏任务进入主线程,遇到 console.log,输出  script start
  • 执行 async1,里面的 await async2 是个同步代码,所以打印了 async2 end;同时,async1 还产生一个微任务(就是 await 后面的代码),放进微任务列表,暂时不执行
  • 继续执行主任务,走到 promise,走里面的同步代码,打印了 Promise,里面的 then 放进微任务列表,暂时不执行
  • 继续执行主任务,执行同步代码 console.log...,打印了 script end
  • 此时微任务列表里,首个微任务是 await 后面的代码,打印了 async1 end;剩下两个微任务是 promise 里的,打印了 promise1、promise2
  • 第一个宏任务执行完毕

第二次事件循环:

  • 执行第二个宏任务 setTimeout,打印了 setTimeout

3. 浏览器更新渲染时机、Vue nextTick

3.1 Event Loop 与 浏览器更新渲染时机

浏览器更新渲染,会在 Event Loop 中的 宏任务、微任务 完成后进行

先宏任务,再微任务,然后再渲染更新

宏任务队列中,如果有大量任务等待执行时,应该将 修改 DOM 作为微任务,这样就能在 本次事件轮询中 更新 DOM(Vue nextTick 就是这么实现的),更快的将 页面变化 呈现给用户

从event loop规范探究javaScript异步及浏览器更新渲染时机 · Issue #5 · aooy/blog · GitHub作者:杨敬卓 转载请注明出处 异步的思考 event loops隐藏得比较深,很多人对它很陌生。但提起异步,相信每个人都知道。异步背后的“靠山”就是event loops。这里的异步准确的说应该叫浏览器的event loops或者说是javaScript运行环境的event loops,因为ECMAScript中没有event loops,event loops是在HTML Standard定义的。 event loops规范中定义了浏览器何时进行渲染更新,了解它有助于...https://github.com/aooy/blog/issues/5

3.2 Vue 为什么要优先使用微任务实现 nextTick

Vue3 nextTick 源码地址:core\packages\runtime-core\src\scheduler.ts

Vue nextTick 的码的优先级判断:Promise > MutationObserver > setImmediate > setTimeout

答:根据 Event Loop 与浏览器更新渲染时机可得:

  • 优先使用 Promise 等微任务,本次事件轮询就能获得更新的 DOM
  • 如果使用宏任务,要到下一次事件轮询中,才能获得更新的 DOM

学习Vue3 第三十五章(Evnet Loop 和 nextTick)_小满zs的博客-CSDN博客在我们学习nextTick 之前需要先了解Event Loop 时间循环机制在我们学js 的时候都知道js 是单线程的如果是多线程的话会引发一个问题在同一时间同时操作DOM 一个增加一个删除JS就不知道到底要干嘛了,所以这个语言是单线程的但是随着HTML5到来js也支持了多线程webWorker 但是也是不允许操作DOM单线程就意味着所有的任务都需要排队,后面的任务需要等前面的任务执行完才能执行,如果前面的任务耗时过长,后面的任务就需要一直等,一些从用户角度上不需要等待的任务就会一直等待,这个从体验角度上来https://xiaoman.blog.csdn.net/article/details/125237755https://github.com/qingzhou729/study/issues/15icon-default.png?t=M85Bhttps://github.com/qingzhou729/study/issues/15

4. Node.js 中的 process.nextTick

4.1 什么是 process.nextTick

process.nextTick 相当于 Node.js 中的 setTimeout,是 Node.js 定义的一种机制,它有自己的 nextTickQueue

process.nextTick 不能完全当作 JavaScript 中的微任务,process.nextTick 执行顺序早于微任务

举个栗子 ~~ 看下面的代码:

console.log("start"); // 1 同步代码

setTimeout(() => {
  console.log("setTimeout"); // 6 第二个宏任务
}, 0);

Promise.resolve().then(() => {
  // 4 第一个微任务,早于 process.nextTick 内部的 promise 放入微任务列表
  console.log("promise");
});

process.nextTick(() => {
  console.log("nextTick"); // 3 Node.js process.nextTick 早于微任务执行
  Promise.resolve().then(() => {
    console.log("promise1"); // 5 第二个微任务
  });
});

console.log("end"); // 2 同步代码

执行结果:start ------ end ------ nextTick ------ promise ------ promise1 ------ setTimeout

4.2 浏览器事件循环 VS  Node 事件循环

浏览器与Node的事件循环(Event Loop)有何区别? - 知乎摘要: 浏览器和Node中Event Loop其实是不相同的。前言本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的。 一、线程与进程1. 概念我们经常说 JS 是单线程执行的,指…https://zhuanlan.zhihu.com/p/54882306

5. setTimeout VS setInterval

  • setTimeout 固定时长后执行
  • setInterval 间隔固定时间后重复执行

5.1 setTimeout / setInterval 的执行时间为啥无法确定呢?

举个栗子~ 

定时器内部代码,需要进行大量计算,或者 DOM 操作,代码执行时间 超过了 定时器设置的时间

再举个栗子~

setTimeout 早早的把 task() 放进等待队列了,但是由于 JavaScript 是单线程的,只能等 sleep() 执行完毕(时间远超 3s),才能执行 task()

setTimeout(() => {
    // 控制台执行 task() 需要的时间,远远超过 3 秒
    task()
}, 3000)

sleep(10000000)

5.2 setTimeout / setInterval 概念容易踩得坑

5.2.1 setTimeout 举个栗子~

setTimeout(fn, 0),可以立即执行吗?答案是不行。

  • setTimeout 指的是:主线程的任务都执行完了,再等 x 秒,才能执行内部代码;
  • 即使主线程为空,也不能立即执行,因为 JavaScript 规定 setTimeout、setInterval 最短时长为 4ms;

5.2.2 setInterval 举个栗子~

对于 setInterval(fn, ms) 来说,他是每过 ms 秒就一定会执行一次 fn 吗?答案是不是。

  • 是每过 ms 秒,会创建一个 fn 进入任务队列;
  • 一旦 setInterval 的回调函数 fn 实际执行时间,超过了延迟时间 ms,那么就完全看不出来有时间间隔了;

5.2.3 setTimeout 模拟实现 setInterval

// 使用闭包实现
function mySetInterval(fn, t) {
  let timer = null;
  function interval() {
    fn();
    timer = setTimeout(interval, t);
  }
  interval();
  return {
    // cancel用来清除定时器
    cancel() {
      clearTimeout(timer);
    }
  };
}

5.2.4 setInterval 模拟实现 setTimeout

function mySetTimeout(fn, time) {
  let timer = setInterval(() => {
    clearInterval(timer);
    fn();
  }, time);
}

// 使用
mySetTimeout(() => {
  console.log(1);
}, 2000);

 

5.3 requestAnimationFrame 浏览器提供的动画 API

5.3.1 setTimeout / setInterval 动画卡顿

setTimeout / setInterval 通过设置一个间隔时间,不断改变 css 实现动画效果,在不同设备上可能会出现卡顿、抖动等现象,这是因为:

  • 不同设备的屏幕,刷新频率可能不同
  • setTimeout / setInterval 只能设置固定的时间间隔,这个时间 与 屏幕刷新间隔可能不同

5.3.2 什么是 requestAnimationFrame

HTML5 新增 API,类似于 setTimeout,window.requestAnimationFrame

requestAnimationFrame 告诉浏览器,在下次重绘之前,执行传入的回调函数(通常是操作 DOM,更新动画的函数),也就是说:刷新频率与显示器的刷新频率保持一致

requestAnimationFrame 是浏览器专门为 动画(DOM 动画、Canvas 动画、 SVG 动画、WebGL 动画) 提供的API,使用该 API 可避免使用setTimeout / setInterval 造成的动画卡顿

5.3.3 setTimeout VS setInterval VS requestAnimationFrame

引擎层面的区别:

  • setTimeout / setInterval 属于 JavaScript 引擎,存在事件轮询
  • requestAnimationFrame 属于 GUI 引擎

JavaScript 引擎与 GUI 引擎是互斥的,也就是说:

  • GUI 引擎在渲染时,会阻塞 JavaScript 引擎的计算;
  • 如果在 GUI 渲染的时候,JavaScript 又同时改变了DOM,就会造成页面渲染不同步;

性能层面的区别:

  • 当页面被隐藏或最小化时,定时器 setTimeout 仍会在后台运行,执行动画任务
  • 当页面被隐藏或最小化时,屏幕刷新任务会被系统暂停,requestAnimationFrame 也会停止

5.3.4 参考文章

setTimeout/setInterval与requestAnimationFrame的区别?_尹成诺的博客-CSDN博客_settimeout setinter requestanmiationfarme提到 setTimeout/setInterval 以及 requestAnimationFrame,大家的第一反应是动画相关的两个 API。什么是web 动画我们来谈谈什么是动画。动画其实是一种假象,是一种不连续的运动以帧的形式呈现给我 们的东西。在二十世纪,通常人们观看的电影其实就是通过胶片记录和投影的。它是以每 秒至少 24 帧的速度形成的视觉上的运动起来的假象。NTSC 广播的标准...https://blog.csdn.net/weixin_40851188/article/details/89669416

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

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

相关文章

线上使用雪花算法生成id重复问题

项目中使用的是hutool工具类库提供的雪花算法生成id方式&#xff0c;版本使用的是5.3.1 <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.1</version></dependency>雪花算法生成id…

10分钟数仓实战之kettle整合Hadoop

1.写在前面 很多朋友在做数仓的ETL的动作的时候&#xff0c;还是喜欢比较易上手的kettle 前面章节有介绍过安装kettle&#xff0c;可以参考 ETL工具--安装kettle_老码试途的博客-CSDN博客_spoon.bat 安装 kettle在Windows系统中对数据的转换、表和文件的转换等&#xff0c;…

Blender 3D环境场景创建教程

Blender 3D环境场景创建教程 学习 Blender 3.2&#xff0c;探索几何节点并创建美妙的 3D 环境 课程英文名&#xff1a;Creating 3D Environments in Blender 2.81 by Rob Tuytel (2019) 此视频教程共8.0小时&#xff0c;中英双语字幕&#xff0c;画质清晰无水印&#xff0c;…

腾讯云从业者基础认证完整笔记

腾讯云从业者基础认证完整笔记 就考这些&#xff0c;干就完事儿了&#xff01;不要介意图多哟&#xff0c;ppt能更好的表达意思呀 一、云计算基础 1.1 数据中心 一般企业要么自建数据中心EDC&#xff0c;EDC分层如下&#xff1a; 要么租用或者托管也就是IDC如下&#xff…

ZYNQ之FPGA学习----EEPROM读写测试实验

1 EEPROM简介 EEPROM (Electrically Erasable Progammable Read Only Memory&#xff0c;E2PROM)即电可擦除可编程只读存储器&#xff0c;是一种常用的非易失性存储器(掉电数据不丢失)。ZYNQ开发板上使用的是AT24C64&#xff0c;通过IIC协议实现读写操作。IIC通信协议基础知识…

Oracle 11g---基于CentOS7

Oracle 11g安装教程 以下步骤基于网络配置完成&#xff0c;并且能连接xshell和xftp工具 文章目录Oracle 11g安装教程1.将oracle压缩包拷贝到安装机器&#xff0c;指定目录中2.安装依赖包3.验证依赖包4.创建oracle用户5.创建oradata目录,解压oracle安装6.修改系统配置参数7.创建…

2023年开始当年授权或转让的知识产权申报高新将不再认可。

前段时间&#xff0c;由国家科技部火炬中心组织全国高新技术企业管理机构召开会议&#xff0c;会议宣导要求加强企业知识产权管理&#xff0c;强调对当年授权或转让的专利&#xff0c;用来申报当年高新将不再认可。 、从多省市反馈的消息显示部分省市执行了该政策。虽然广东暂…

Java 2022圣诞树+2023元旦倒计时打包一起领走

2022最后一个月充满了期待&#xff0c;平安夜、圣诞节、元旦节&#xff1b;2023年也是一个早年&#xff0c;因此关于程序方面的浪漫&#xff0c;大家应该趁早准备。下面我将分享一个元旦的倒计时和圣诞树的绘制核心代码。大家可以依据自身的需求&#xff0c;稍微调整即可用。 …

振弦渗压计怎样安装?振弦式渗压计工作原理

振弦渗压计是一种长期测量混凝土或地基内的孔隙(渗透)水压力&#xff0c;并可同步测量埋设点温度。适用于大坝工程安全监测、尾矿库工程安全监测、各类公路、桥梁、隧洞安全监测、土工建筑物基坑安全监测等。    1、设备介绍 通过不断的生产工艺技术的积累&#xff0c;采用…

vscode给docker内部的的ros工程代码打断点

背景 打断点debug虽然不能直观看到变量在时间轴上的整体变化曲线&#xff0c;但是其针对某一帧问题数据&#xff0c;暂停后一步步单步执行监视每个变量的变化&#xff0c;方便直观的判断每一步逻辑的正确性&#xff0c;即使这个变量结构再复杂也能直接监视&#xff0c;可以准确…

推荐5款压箱底的小工具软件

今天要给大家推荐5款压箱底的宝贝软件了&#xff0c;百度搜索一下就能找到下载链接了。 1.阅读笔记——BookxNote BookxNote 是一款 PDF 和 EPUB 阅读笔记软件&#xff0c;集阅读、笔记、批注、思维导图、划词翻译等于一体&#xff0c;可以边读边记。它的标注功能非常全&…

惊 GitHub首次开源,在国内外都被称为分布式理论+实践的巅峰之作

前言 蓦然回首自己做开发已经十年了&#xff0c;这十年中我获得了很多&#xff0c;技术能力、培训、出国、大公司的经历&#xff0c;还有很多很好的朋友。但再仔细一想&#xff0c;这十年中我至少浪费了五年时间&#xff0c;这五年可以足够让自己成长为一个优秀的程序员&#…

自有服务器(2台)被 kthreaddk木马挖矿解决过程(实操)不重启服务器

第一台服务器&#xff1a; #查看进程和CPU使用情况 top #查找相关联的进程 systemctl status 326858 #查看下所的 端口号和进程&#xff0c;发现有异常端口和进程 netstat -ntpl #杀死关联进程&#xff08;异常进程&#xff09;&#xff0c; kill -9 2900707 #杀死主进程&a…

Redis高可用之主从复制架构(第一部分)

引言 之前的文章 Redis持久化策略AOF、RDB详解及源码分析&#xff0c;我们介绍了Redis中的数据持久化技术&#xff0c;包括 RDB快照 和 AOF日志以及混合持久化 。有了持久化技术&#xff0c;我们就不用担心因Redis所在服务机器宕机&#xff0c;导致数据丢失。但是&#xff0c;…

四阶龙格库塔法求解一次常微分方程组(python实现)

四阶龙格库塔法求解一次常微分方程组一、前言二、RK4求解方程组的要点1. 将方程组转化为RK4求解要求的标准形式2. 注意区分每个方程的独立性三、python实现RK4求解一次常微分方程组1. 使用的方程组2. python代码3. 运行结果一、前言 之前在博客发布了关于使用四阶龙格库塔方法…

字节测试开发最牛教程,全栈Jmeter_性能测试(总结)

Jmeter_性能测试(4)&#xff1a; 性能测试脚本的优化 以PHP论坛为例&#xff1a;http://47.107.178.45/phpwind/ 根据上一篇的性能测试(3&#xff09;的脚本进行优化&#xff1b;见下图&#xff1a; 如上图中&#xff0c;把发帖和回帖的事务添加到随机控制器中&#xff0c;登…

一例cobalt Strike 反射式注入payload的分析

一例cobalt Strike payload 反射式dll注入的分析 QakBot(Qbot)与cobalt Strike恶意流量样本分析 | Demon (ggsec.cn)这篇博客中末尾提到了一个cobastrick的payload&#xff0c;这是一段shellcode&#xff0c;主要功能是的解密出一个dll&#xff0c;采用反射式注入的方式启动这…

EC 中的Keyboard Controller

Keyboard Controller简称KBC,它是EC芯片中一个用于处理Keyboard、Mouse的模块,也可以说,它只是一个通道,因为最后处理数据的还是交给EC 8032处理器去处理。KBC只处理挂在EC PS/2接口上的设备,假如接了个usb键盘或鼠标,那可不关它的事。PS/2设备只有两种,即Keyboard和Mou…

React 的设计理念(React 哲学)

文章目录React 的设计理念 的理解解决 CPU 瓶颈解决 IO 瓶颈React 的设计理念 的理解 从 React 官网中的 React 哲学文档中&#xff0c;可以看出 React 目的是实现快速响应 影响快速响应的因素&#xff1a;计算能力和网络延迟&#xff0c;即 CPU 和 IO 的瓶颈 解决 CPU 瓶颈 …

再见 ETH India 2022 建设者们 让我们一起回顾这个美好的建设周

很难超越的1700名黑客马拉松比赛&#xff0c;但是以太坊社区出现并打破了ETH India 2022 的新记录。来自321个城市的2000名与会者在短短的一个周内构建并部署了多达459个项目到以太坊生态系统中。你可能错过了过去一周发生的一切&#xff0c;但幸运的是&#xff0c;我们收集了所…