精简版
0
0
0
云栖社区>博客>正文
avillin 2019-12-04 18:30:02 浏览 4028
- JavaScript
- promise
JS 事件
- setTimeout
- EventLoop
事件循环机制
宏观任务
微观任务
事件运行机制
事件循环
展开阅读全文
今天简单说下 JS 的事件循环机制, 我们都知道, JavaScript 是单线程语言, 它的核心, 也是因为它的单线程.
有很多小白不清楚 EventLoop 到底是什么, 按照中文翻译, 就是事件循环, 那 JS 到底是怎样将同步和异步进行处理的.
这篇文章, 就简单说一说, JS 的单线程处理, 也就是同步和异步的代码是怎样走向的.
一, 同步和异步:
所有的线程, 都是有同步队列, 和异步队列, 立即执行的任务队列, 这些都是属于同步任务, 比如一个简单的函数; 那么像请求接口发送 Ajax, 发送 promise, 或时间计时器等等, 这些就是异步任务.
二, 任务队列 - 事件循环:
同步任务会立刻执行, 进入到主线程当中, 异步任务会被放到任务队列 (Event Queue) 当中.
Event Queue 单词的意思就是任务队列.
等待同步代码执行完毕后, 返回来, 再将异步中的任务放到主线程中执行, 反复这样的循环, 这就是事件循环.
也就是先执行同步, 返回来按照异步的顺序再次执行
我们看下面这个代码会打印出什么:
- console.log('开始 111');
- setTimeout(function() {
- console.log('setTimeout111');
- }, 0);
- Promise.resolve().then(function() {
- console.log('promise111');
- }).then(function() {
- console.log('promise222');
- });
- console.log('开始 222');
我们猜想一下上面的代码, 会怎样打印? 我们知道, 肯定是先走同步的代码, 从上往下, 先打印 "开始 111", 再打印 "开始 222".
中途的三个异步, 进入到了异步队列, 等待同步执行完(打印完), 返回来再执行异步, 所以是后打印出来.
打印的结果先放一放, 我们稍后回来再说. 现在我们中途插播一段知识点:
三, 宏观任务和微观任务(先执行微观任务, 再执行宏观任务):
在事件循环中, 每进行一次循环操作称为 tick,tick 的任务处理模型是比较复杂的, 里边有两个词: 分别是 Macro Task (宏任务)和 Micro Task(微任务).
简单来说:
宏观任务主要包含: setTimeout,setInterval,script(整体代码),I/O,UI 交互事件, setImmediate(Node.JS 环境)
微观任务主要包括: Promise,MutaionObserver,process.nextTick(Node.JS 环境)
规范: 先执行微观任务, 再执行宏观任务
那么我们知道了, Promise 属于微观任务, setTimeout,setInterval 属于宏观任务, 先执行微观任务, 等微观任务执行完, 再执行宏观任务. 所以我们再看一下这个代码:
- console.log('开始 111');
- setTimeout(function() {
- console.log('setTimeout111');
- }, 0);
- Promise.resolve().then(function() {
- console.log('promise111');
- }).then(function() {
- console.log('promise222');
- });
- console.log('开始 222');
我们按照步骤来分析下:
1, 遇到同步任务, 直接先打印 "开始 111".
2, 遇到异步 setTimeout , 先放到队列中等待执行.
3, 遇到了 Promise , 放到等待队列中.
4, 遇到同步任务, 直接打印 "开始 222".
5, 同步执行完, 返回执行队列中的代码, 从上往下执行, 发现有宏观任务 setTimeout 和微观任务 Promise , 那么先执行微观任务, 再执行宏观任务.
所以打印的顺序为: 开始 111 , 开始 222 , promise111 , promise222 , setTimeout111 .
同理, 我们再来分析一个代码:
- console.log('开始 111');
- setTimeout(function () {
- console.log('timeout111');
- });
- new Promise(resolve => {
- console.log('promise111');
- resolve();
- setTimeout(() => console.log('timeout222'));
- }).then(function () {
- console.log('promise222')
- })
- console.log('开始 222');
分析一下:
1, 遇到同步代码, 先打印 "开始 111" .
2, 遇到 setTimeout 异步, 放入队列, 等待执行 .
3, 中途遇到 Promise 函数, 函数直接执行, 打印 "promise111".
4, 遇到 setTimeout , 属于异步, 放入队列, 等待执行.
5, 遇到 Promise 的 then 等待成功返回, 异步, 放入队列.
6, 遇到同步, 打印 "开始 222".
7, 执行完, 返回, 将异步队列中的代码, 按顺序执行. 有一个微观任务, then 后的, 所以打印 "promise222", 再执行两个宏观任务 "timeout111" "timeout222".
所以, 打印的顺序为: 开始 111 , promise111 , 开始 222 , promise222 , timeout111 , timeout222 .
先执行主任务, 把异步任务放入循环队列当中, 等待主任务执行完, 再执行队列中的异步任务. 异步任务先执行微观任务, 再执行宏观任务. 一直这样循环, 反复执行, 就是事件循环机制.
扩展[宏观任务和微观任务] :
在宿主 (如浏览器) 中发起的任务是宏观任务, 如 setTimeout,JS 引擎中发起的任务是微观任务, 如 Promise.JS 执行时, 微观任务优先于宏观任务.
来源: https://yq.aliyun.com/articles/737952