EventLoop 的理解
一般前端了解eventloop的时候, 最想知道代码执行的先后顺序,并非分析EventLoop。所以这里先说总结。 为什么?因为面试常考这个😂,因为分析和听懂分析都费劲。
##一、总结
- 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)
}
fn1()
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源码分析
了解过,可以跳过
四、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
}
})
}
}
五、promise 和 async await
怎么理解,特简单,不用想那么负责。就当 aysnc await 其实就是 promise 的语法糖。
验证:
function a () {
console.log('a')
return 'a'
}
async function b () {
const res = await a('a')
return res
}
b().then(res => {
console.log('this is', res)
})
const p = Promise.resolve(b)
console.log('b() is promise', b() instanceof Promise)
console.log('p is promise', p instanceof Promise)
// b() is promise true
// p is promise true
// this is a
总结
分析到这里,基本了解的 promise.then 和 resolve 的实现。reject 相当于 promise 的 resolve 的翻版,catch、all、race 就不在话下,简而言之,promise 源码最终重要的封装是 promise.then 和 resolve.
转载自:https://juejin.cn/post/6844903837552082951