通常前端了解eventloop的時候, 最想知道代碼執行的前後順序,並不是分析EventLoop。因此這裏先說總結。 爲何?由於面試常考這個😂,由於分析和聽懂分析都費勁。javascript
##1、總結html
ps: 對純前端而言,掌握Promise.prototype.then、async await 和 setTimeout 的區別就能夠了。另外,Promise、async await是能夠轉換的,可是瀏覽器版本問題,async await 的優先級可能高於 Promise(能夠忽略這種狀況)。前端
測試題目:java
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
複製代碼
分析:git
代碼轉換分析github
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…面試
瞭解過,能夠跳過設計模式
看這篇:juejin.im/entry/59996… 以爲分析有點囉嗦,沒有提取關鍵信息promise
瞭解過,能夠跳過瀏覽器
MDN文檔 的 Promise 執行流程圖:
瞭解代碼第一步,必定要知道他的設計模式,可以節約至關看代碼時間。Promise 代碼 最主要的模式,觀察者模式
封裝一個簡易的Promise的 resolve 和 then,便於理解 Pomise 的封裝解構
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 爲何不能在全局環境下,只能在函數內?緣由是爲了加入異步隊列。
上面的封裝的代碼,簡單理解 Promise 封裝的解構,如今梳理 Promise的解構,可知道 promise 的根本的兩個方法: then 和 _resolve , _queues、_status。reject 、catch等都是在此基礎上上進行二次封裝。下列數列梳理幾個點:
Promise
時的處理方式setTimeout(run)
中 ,先進先出執行異步隊列_queues
是裏面是多個觀察者,這裏根據其餘人說的,要實現一個 1 對 1 的觀察者模式。源碼地址--> 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
}
})
}
}
複製代碼
怎麼理解,特簡單,不用想那麼負責。就當 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.