内容简介:一般前端了解eventloop的时候,##一、总结ps:对纯前端而言,掌握Promise.prototype.then、async await 和 setTimeout 的区别就可以了。另外,Promise、async await是可以转换的,但是浏览器版本问题,async await 的优先级可能高于 Promise(
一般前端了解eventloop的时候, 最想知道代码执行的先后顺序 ,并非分析EventLoop。 所以这里先说总结 。 为什么?因为面试常考这个:joy:,因为分析和听懂分析都费劲。
##一、总结
- js 单线程缺点,容易出现"假死"(如alert()之后,dom不渲染)。优点:保证 dom 的渲染不易出错。
- 解决"假死"的问题?其他语言,如 java 采用多线程去解决,避免占用计算机空间太大,dom 渲染问题易出错问题存在。所以js 采用单线程+异步解决方案。
- 单线程 + 异步的实现方式叫做——EventLoop
- 加入 异步队列 方式有两种,第一种是宏任务;第二种是微任务。 微任务加入异步队列的优先级宏任务,且是先进先出原则 ,说得比较绕,简而言之就是, 微任务执行完,才会执行宏任务 。
- 微任务:Promise.prototype.then、async await、Process.nextTick(Node独有)、Object.observe(废弃)、MutationObserver
- 宏任务:script全部代码、setTimeout、setInterval、setImmediate(浏览器暂时不支持,只有IE10支持,具体可见MDN)、I/O、UI Rendering。
- 执行的优先级即, 同步函数(主线程上) -> 异步队列·微任务 -> 异步队列·宏任务
ps:对纯前端而言,掌握Promise.prototype.then、async await 和 setTimeout 的区别就可以了。另外,Promise、async await是可以转换的,但是浏览器版本问题,async await 的优先级可能高于 Promise( 可以忽略这种情况 )。
测试题目:
const fn1 = await function () { await fn2() console.log(1) } async function fn2 () { await console.log(2) } setTimeout(function () { console.log(3) }) new Promise(function (resolve, reject) { console.log(4) resovle() }).then(function () { console.log(5) }).then(function () { console.log(6) }) // 答案 // 2 // 4 // 1 // 5 // 6 // 3 复制代码
分析:
- 2 和 4 是同步函数
- 1、5 和 6 是微任务,异步队列的顺序是1、5、6
- 3 是宏任务
代码转换分析
function fn1 () { new Promise(function(resolve, reject) { console.log(2) resolve() }).then(function () { console.log(1) }) } fn1() setTimeout(function () { console.log(3) }) new Promise(function (resolve) { console.log(4) resolve() }).then(function () { console.log(5) }).then(function () { console.log(6) }) 复制代码
二、内存分析和缘由
阮一峰eventloop www.ruanyifeng.com/blog/2013/1… 看内存分析的这篇 github.com/baiyuze/not…
了解过,可以跳过
三、封装Promise源码分析
看这篇: juejin.im/entry/59996… 觉得分析有点啰嗦,没有提取关键信息
了解过,可以跳过
四、uml 类图分析Promise
MDN文档 的 Promise 执行流程图:
第 1 步 用到的 设计模式 和 理解 promise 的简易结构
了解代码第一步,一定要知道他的设计模式,能够节约相当看代码时间。Promise 代码 最主要的模式, 观察者模式
封装一个简易的Promise的 resolve 和 then ,便于理解 Pomise 的封装解构
- then 从语法结构上讲是然后的意思,但在封装上,是将函数加入异步队列,并返回一个 Promise 的一个类似对象
- resolve 执行异步队列
class Que { _queueLit = [] constructor (handler) { handler.call(this, this._resolve) } _queue (cb) { setTimeout(cb) } _resolve = () => { const { _queueLit, _queue } = this const resolve = function () { let cb while (cb = _queueLit.shift()) { cb() } } _queue(resolve) } then (cb) { this._queueLit.push(cb) return this } } // test setTimeout(() => { console.log(4) }); new Que(function (resolve) { console.log(1) resolve() }).then(function () { console.log(2) }).then(function () { console.log(3) }) // 结果 // 1 // 4 // 2 // 3 复制代码
后记:不然发现 setTimeout 执行的 callback 为什么是全局的,以及 await 为什么不能在全局环境下,只能在函数内?原因是为了加入异步队列。
第 2 步 promise 处理8关键点(第 3 点 最为重要)
上面的封装的代码,简单理解 Promise 封装的解构,现在梳理 Promise的解构,可知道 promise 的根本的两个方法: then 和 _resolve , _queues、_status。reject 、catch等都是在此基础上上进行二次封装。下列数列梳理几个点:
- then 接受的函数,是
Promise
时的处理方式 - then 接受的函数,是同步函数时的处理方式
- then 返回一个 Promise 实例,将该实例要执行的 _resolve 放到上一个 Promise 实例的异步队列中即将执行的异步函数中
- _resolve 传递参数的value 是一个Promise 时的处理方式
- _resolve 放到 eventloop 中,即
setTimeout(run)
中 ,先进先出执行异步队列 - _status 状态改变只在 _resolve 中改变
- _status 状态判断只在then中执行
- 利用自责链之后,每个Promise 的 _queues 只有一个元素,
_queues
是里面是多个观察者,这里根据其他人说的,要实现一个 1 对 1 的观察者模式。
第 3 步 从完整版的Promise提取关键代码
源码地址--> coderlt.coding.me/2016/12/04/…
uml 类图
拿到源码,将源码错误检测、不需要分析的方法通通干掉,避免混淆视听,得到如下代码:
// 判断变量否为function const isFunction = variable => typeof variable === 'function' const StatusType = { PENDING: 'PENDING', FULFILLED: 'FULFILLED', } class MyPromise { _status = StatusType.PENDING // 添加状态 _value = undefined // 添加状态 _fulfilledQueues = [] // 添加成功回调函数队列 constructor(handle) { handle(this._resolve.bind(this)) } _resolve(val) { const run = () => { if (this._status !== StatusType.PENDING) return const runFulfilled = (value) => { let cb while (cb = this._fulfilledQueues.shift()) { cb(value) } } if (val instanceof MyPromise) { const resolvePromise = val resolvePromise.then(value => { this._value = value this._status = StatusType.FULFILLED runFulfilled(value) }) } else { this._value = val this._status = StatusType.FULFILLED runFulfilled(val) } } setTimeout(run) } then(onFulfilled) { const { _value, _status } = this // 返回一个新的Promise对象 return new MyPromise((onFulfilledNext) => { // 封装一个成功时执行的函数 let fulfilled = value => { let res = onFulfilled(value) if (res instanceof MyPromise) { res.then(onFulfilledNext) } else { // 下一个 promise 的 resolve 方法的执行 onFulfilledNext(res) } } switch (_status) { case StatusType.PENDING: /* 至关重要的代码 */ this._fulfilledQueues.push(fulfilled) /* 至关重要的代码 end */ break case StatusType.FULFILLED: fulfilled(_value) break } }) } } 复制代码
以上所述就是小编给大家介绍的《EventLoop 的理解》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 理解原型其实是理解原型链
- 要理解深度学习,必须突破常规视角去理解优化
- 深入理解java虚拟机(1) -- 理解HotSpot内存区域
- 荐 【C++100问】深入理解理解顶层const和底层const
- 深入理解 HTTPS
- 深入理解 HTTPS
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。