文章目录

  • 一、事件循环
    • 任务队列
    • 宏任务和微任务
    • 循环机制
      • 简单示例
  • 二、Async/Await
    • 1. async
    • 2. await
    • 3. 原理
    • 4. 示例(红字分析为关键)

一、事件循环

任务队列

  • 同步任务:立即执行的任务,同步任务一般会直接进入到主线程中执行;
  • 异步任务:异步执行的任务,比如ajax网络请求,setTimeout 定时函数,异步任务会通过任务队列的机制(先进先出的机制)来进行协调。

主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行。

上述过程的不断重复就是我们说的 Event Loop (事件循环)

宏任务和微任务

  • Macro Task (宏任务):script( 整体代码)、setTimeout、setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境)
    在ECMAScript中,macrotask可称为task
  • Micro Task(微任务):Promise、MutaionObserver、process.nextTick(Node.js 环境)
    在ECMAScript中,microtask称为jobs
    在node环境下,process.nextTick的优先级高于Promise。简单理解:在宏任务结束后会先执行微任务队列中的nextTickQueue部分,然后才会执行微任务中的Promise部分。

每个宏任务结束后, 都要清空所有的微任务

循环机制

1.在此次 tick 中选择最先进入队列的任务( oldest task ),如果有则执行(一次)
2.检查是否存在 (Microtasks)微任务 ,如果存在则不停地执行,直至清空 微任务队列(Microtask Queue)
3.更新 render
4.主线程重复执行上述步骤


通俗来讲,就是先执行主线程的任务,然后把所有微任务执行完,才执行宏任务,算一次tick。
开启下一轮tick后,把新产生的所有微任务执行完,才可以执行新的宏任务……如图循环往复

简单示例

console.log('script start');setTimeout(function() {console.log('setTimeout');
}, 0);Promise.resolve().then(function() {console.log('promise1');
}).then(function() {console.log('promise2');
});console.log('script end');
  1. 整体script作为第一个宏任务进入主线程,遇到console.log('script start');,输出script start
  2. 遇到setTimeout,其回调函数被分发到宏任务Event Queue 中
  3. 遇到Promise,其then函数被分到微任务Event Queue中,记为then1,然后又遇到then函数,将其分配到微任务Event Queue中,记为then2
  4. 遇到console.log('script end');,输出script end

现在宏任务队列和微任务队列如下:

  1. 执行微任务,首先执行then1,输出 promise1;
    然后执行 then2,输出 promise2 ,清空所有微任务
  2. 执行宏任务 setTimeout ,输出 setTimeout

至此,输出的顺序是:script start, script end, promise1, promise2, setTimeout

二、Async/Await

1. async

当我们在函数前使用async的时候,使得该函数返回的是一个Promise对象
async的函数会在这里帮我们隐式使用Promise.resolve(1)

async function test() {return 1
}

等价于下面的代码

function test() {return new Promise(function(resolve, reject) {resolve(1)})
}

2. await

await表示等待,是右侧「表达式」的结果,这个表达式的计算结果可以是 Promise 对象的值或者一个函数的值(换句话说,就是没有特殊限定)。并且只能在带有async的内部使用

使用await时,会从右往左执行,当遇到await时,会阻塞函数内部处于它后面的代码,去执行该函数外部的同步代码,当外部同步代码执行完毕,再回到该函数内部执行剩余的代码,并且当await执行完毕之后,会先处理微任务队列的代码

如果还是很懵的话可以看一下示例分析过程,红字非常关键!!!

3. 原理

async/await是语法糖,用async标记的函数,在其内部遇到await标记的逻辑时,会暂时返回,不执行后续的逻辑,等await内部的逻辑处理完毕后,再继续走await后面的逻辑。

这不就是es6定义的 generator函数 (如果不了解可以先学习generator相关知识)?
async/await将标记的函数转换成了生成器

generator和async/await区别

  • async/await自带执行器,不需要手动调用next()就能自动执行下一步
  • async函数返回值是Promise对象,而Generator返回的是生成器对象
  • await能够返回Promise的resolve/reject的值

模拟实现思想(非具体实现):

function run(gen) {var g = gen()                     //由于每次gen()获取到的都是最新的迭代器,因此获取迭代器操作//要放在_next()之前,否则会进入死循环function _next(val) {             //封装一个方法, 递归执行g.next()var res = g.next(val)           //获取迭代器对象,并返回resolve的值if(res.done) return res.value   //递归终止条件:判断done属性,后续是否没有语句内容了res.value.then(val => {         //Promise的then方法是实现自动迭代的前提_next(val)                    //等待Promise完成就自动执行下一个next,并传入resolve的值})}_next()  //第一次执行
}

4. 示例(红字分析为关键)

<script>async function async1() {console.log( 'async1 start' )await async2()console.log( 'async1 end' )}async function async2() {console.log( 'async2' )}console.log( 'script start' )setTimeout( function () {console.log( 'setTimeout' )}, 0 )async1();new Promise( function ( resolve ) {console.log( 'promise1' )resolve();} ).then( function () {console.log( 'promise2' )} )console.log( 'script end' )
</script>

使用事件循环机制分析:()

  1. 首先执行主程序script同步代码:console.log( 'script start' )

  2. 遇到setTimeout,推入宏任务队列

  3. 执行async1(),它也是同步的,只是返回值是Promise,在内部会直接执行console.log( 'async1 start' )

  4. 在async1中遇到await async2(),然后执行async2(),打印console.log( 'async2' )

  5. 又因 当遇到await时,阻塞此行后面的代码,所以会去外部执行主程序未执行完的同步代码

  6. 主程序中遇到 new Promise并进入,resolve所以直接打印console.log( 'promise1' ),并将.then放入事件循环的微任务队列

  7. 跳出promise,继续执行主程序同步代码,打印console.log( 'script end' )

  8. 此时外部同步代码全部执行完毕,回到async1()内部。
    【由于async2()其实是返回一个Promise, await async2()相当于获取它的值,其实就相当于这段代码Promise.resolve(undefined).then((undefined) => {}),所以.then会被推入微任务队列】
    所以现在微任务队列会有两个任务:

    注意!!处理余下代码之前,先处理微任务队列!
    打印console.log( 'promise2' ),后面一个.then不会有任何打印,但是会执行,所以此时微任务队列清空:

  9. 清空微任务队列后,再执行await后面的代码!
    打印console.log( 'async1 end' )

  10. 进入第二次事件循环tick,执行宏任务队列, 打印console.log( 'setTimeout' ),此时事件完全清空:

事件循环机制 + ES7:Async/Await(基于generator原理实现)附详细示例分析相关推荐

  1. js事件循环机制(await-async-事件循环)

    await和async 异步函数 async function async关键字用于声明一个异步函数: async是asynchronous单词的缩写,异步.非同步: sync是synchronous ...

  2. 事件循环机制EventLoop

    事件循环机制EventLoop Event Loop即事件循环,是解决javaScript单线程运行阻塞的一种机制. 一.EventLoop的相关概念 1.堆(Heap) 堆表示一大块非结构化的内存区 ...

  3. JS 常见面试题 - 事件循环机制

    一.浏览器JS异步执行的原理 一般常说js是一门单线程语言,那为什么可以异步执行且不发生阻塞呢? 常说的JS是单线程语言,是因为执行JS的引擎是单线程的,而浏览器本身是多线程的 浏览器主要含有: js ...

  4. 详解浏览器和Node的事件循环机制及区别

    关于事件循环机制(详解) 前言 一.浏览器的事件循环机制 二.Node的事件循环机制 三.两者的区别 前言 JS是单线程的脚本语言,即在同一时间只能做一件事.为了协调时间.用户交互.脚本.UI渲染和网 ...

  5. 浏览器事件循环机制与Vue nextTick的实现

    浏览器事件循环机制 先上一段简单的代码 console.log('aa'); setTimeout(() => { console.log('bb')}, 0); Promise.resolve ...

  6. 前端面试之事件循环机制

    一.事件循环是什么? 首先,JS是单线程的,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环机制.在JS中把任务分为同步和异步,同步任务和异步任务的执 ...

  7. EventLoop 事件循环机制

    1. EventLoop 概念 Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理. 因为JavaScrip ...

  8. QT消息/事件循环机制与多线程的关系

    关于Qt子线程和消息循环 一.QT消息/事件循环机制 Qt作为一个可视化GUI界面操作系统,是基于事件驱动的,我们程序执行的顺序不再是线性,而是由一个个应用程序内部或外部的事件进行驱动,无事件时便阻塞 ...

  9. Event loop/浏览器的事件循环机制

    一.Event loop引入 1.js为什么是单线程的? 之前我们已经学习了浏览器的关键渲染路径,知道了网页内容交给浏览器后浏览器是如何解析.渲染.绘制的,也提到了浏览器是多线程的,js是单线程的,那 ...

最新文章

  1. html and js 的隔行换背景色表格实例详解
  2. 部署高可用的Lync Server 2013 Part 4 部署高可用的文件共享DFS
  3. Axis通过wsdd部署Web Service
  4. LeetCode 234. 回文链表(快慢指针+链表反转)
  5. M1 三合一机床电路故障检测
  6. GitHub 版本控制 项目托管 04 创建GitHub远程仓库
  7. 茅侃侃,80后技术人与你同在 | 一周业界事
  8. php更改html内容,请问你们怎么将html的文件的内容改变为php
  9. 缺少公钥问题的解决方法[转]
  10. 00048_this关键字
  11. T60 Fan Error 解决办法.转自ZOL产品论坛-作者zxymb
  12. 使用jsMind实现可拖拽思维导图
  13. 怎么爬取全量企业工商数据(思路)
  14. PT、CT、CVT各是什么意思?
  15. Unity - Unable to merge android manifests.
  16. 液晶显示器LCD与LED的区别
  17. 同样是90后别人家的孩子已经是年薪百万算法工程师,而你呢?
  18. 浅谈大型互联网的企业入侵检测及防护策略
  19. 图神经网络框架DGL实现Graph Attention Network (GAT)笔记
  20. IP地址非你在商户平台设置的可用IP地址

热门文章

  1. 浅谈数据结构-二叉排序树
  2. HTML5-A*寻路算法2
  3. @property and @synthesize区别
  4. 局域网内数据采集总结(四)
  5. linux mysql 集群安装配置_linux下mysql集群的安装
  6. Array(数组-转树)
  7. 7-2 最大流 加强版 (20 分)
  8. linux+应用程序高级编程,linux-----shell高级编程----grep应用
  9. 2020字符串的插入(C++,stringchar*)
  10. java web中出现莫名错误,出现错误标识和红线但不影响运行。