1.高級 WEB 面試會讓你手寫一個Promise,Generator 的 PolyFill(一段代碼);
2.在寫以前咱們簡單回顧下他們的做用;
3.手寫模塊見PolyFill.vue
源碼地址請戳,原創碼字不易,歡迎 starreact
若是以爲看文章太囉嗦,能夠直接 git clone ,直接看代碼git
Promise 你們應該都用過,ajax 庫就是利用 Promise封裝的;
做用主要是解決地獄回調問題.github
new Promise((resolve,reject)=>{
resolve('這是第一個 resolve 值')
}).then((data)=>{
console.log(data) //會打印'這是第一個 resolve 值'
}).catch(()=>{
})
new Promise((resolve,reject)=>{
reject('這是第一個 reject 值')
}).then((data)=>{
console.log(data)
}).catch((data)=>{
console.log(data) //會打印'這是第一個 reject 值'
})
複製代碼
Promise.resolve('這是第二個 resolve 值').then((data)=>{
console.log(data) // 會打印'這是第二個 resolve 值'
})
Promise.reject('這是第二個 reject 值').then((data)=>{
console.log(data)
}).catch(data=>{
console.log(data) //這是第二個 reject 值
})
複製代碼
表示多個 Promise 都進入到 FulFilled 或者 Rejected 就會執行面試
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([pOne, pTwo, pThree]).then(data => {
console.log(data); // [1, 2, 3] 正常執行完畢會執行這個,結果順序和promise實例數組順序是一致的
}, err => {
console.log(err); // 任意一個報錯信息
});
複製代碼
表示多個 Promise 只有一個進入到 FulFilled 或者 Rejected 就會執行ajax
const pOne = new Promise((resolve, reject) => {
resolve(1);
});
const pTwo = new Promise((resolve, reject) => {
resolve(2);
});
const pThree = new Promise((resolve, reject) => {
// resolve(3);
});
Promise.race([pOne, pTwo, pThree]).then(data => {
console.log(data); // 1 只要碰到FulFilled 或者 Rejected就會中止執行
}, err => {
console.log(err); // 任意一個報錯信息
});
複製代碼
1.Promise接受一個函數handle做爲參數,handle包括resolve和reject兩個是函數的參數
2.Promise 至關於一個狀態機,有三種狀態:pending,fulfilled,reject,初始狀態爲 pending
3.調用 resolve,狀態由pending => fulfilled
4.調用reject,會由pending => rejected
5.改變以後不會變化數組
1.接受兩個參數,onFulfilled和onRejected可選的函數promise
2.不是函數必須被忽略bash
3.onFullfilled: A.當 promise 狀態變爲成功時必須被調用,其第一個參數爲 promise 成功狀態傳入的值( resolve 執行時傳入的值; B.在 promise 狀態改變前其不可被調用
C.其調用次數不可超過一次koa
4.onRejected:做用和onFullfilled相似,只不過是promise失敗調用
5.then方法能夠鏈式調用 A.每次返回一個新的Promise
B.執行規則和錯誤捕獲:then的返回值若是是非Promise直接做爲下一個新Promise參數,若是是Promise會等Promise執行
// 返回非Promise
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一個普通值
return '這裏返回一個普通值'
})
promise2.then(res => {
console.log(res) //1秒後打印出:這裏返回一個普通值
})
// 返回Promise
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, 1000)
})
promise2 = promise1.then(res => {
// 返回一個Promise對象
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('這裏返回一個Promise')
}, 2000)
})
})
promise2.then(res => {
console.log(res) //3秒後打印出:這裏返回一個Promise
})
複製代碼
C. onFulfilled 或者onRejected 拋出一個異常 e ,則 promise2 必須變爲失敗(Rejected),並返回失敗的值 e
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then(res => {
throw new Error('這裏拋出一個異常e')
})
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) //1秒後打印出:這裏拋出一個異常e
})
複製代碼
D.onFulfilled 不是函數且 promise1 狀態爲成功(Fulfilled), promise2 必須變爲成功(Fulfilled)並返回 promise1 成功的值
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000)
})
promise2 = promise1.then('這裏的onFulfilled原本是一個函數,但如今不是')
promise2.then(res => {
console.log(res) // 1秒後打印出:success
}, err => {
console.log(err)
})
複製代碼
E.onRejected 不是函數且 promise1 狀態爲失敗(Rejected),promise2必須變爲失敗(Rejected) 並返回 promise1 失敗的值
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('fail')
}, 1000)
})
promise2 = promise1.then(res => res, '這裏的onRejected原本是一個函數,但如今不是')
promise2.then(res => {
console.log(res)
}, err => {
console.log(err) // 1秒後打印出:fail
})
複製代碼
F.resolve和reject結束一個Promise的調用
G.catch方法,捕獲異常
其實複雜的是在then的異常處理上,不過不用急,邊看邊理解
1.靜態resolve方法 參照1.3.1用法2
2.靜態reject方法 參照1.3.1用法2
3.靜態all方法 參照1.3.1用法3
4.靜態race方法
參照1.3.1用法4
5.自定義done方法
Promise 對象的回調鏈,無論以then方法或catch方法結尾,要是最後一個方法拋出錯誤,都有可能沒法捕捉到(由於 Promise內部的錯誤不會冒泡到全局)
所以,咱們能夠提供一個done方法,老是處於回調鏈的尾端,保證拋出任何可能出現的錯誤
Promise.prototype.done = function (onFulfilled, onRejected) {
this
.then(onFulfilled, onRejected)
.catch(function (reason) {
// 拋出一個全局錯誤
setTimeout(() => {
throw reason
}, 0)
})
}
複製代碼
6.自定義finally方法
finally方法用於指定無論Promise對象最後狀態如何,都會執行的操做
它與done方法的最大區別,它接受一個普通的回調函數做爲參數,該函數無論怎樣都必須執行
Promise.prototype.finally = function (callback) {
let P = this.constructor
return this.then(
value => P.resolve(callback()).then(() => value),
reason => P.resolve(callback()).then(() => {
throw reason
})
)
}
複製代碼
class MyPromise {
constructor (handle) {
// 判斷handle函數與否
if (typeof handle!=='function') {
throw new Error('MyPromise must accept a function as a parameter')
}
// 添加狀態
this._status = 'PENDING'
// 添加狀態
this._value = undefined
// 執行handle
try {
handle(this._resolve.bind(this), this._reject.bind(this))
} catch (err) {
this._reject(err)
}
}
// 添加resovle時執行的函數
_resolve (val) {
if (this._status !== 'PENDING') return
this._status = 'FULFILLED'
this._value = val
}
// 添加reject時執行的函數
_reject (err) {
if (this._status !== 'PENDING') return
this._status = 'REJECTED'
this._value = err
}
}
複製代碼
回顧一下,初級版實現了1,2,3這三點功能,怎麼樣仍是so-easy吧!
1.因爲 then 方法支持屢次調用,咱們能夠維護兩個數組,將每次 then 方法註冊時的回調函數添加到數組中,等待執行 在初級的基礎上加入成功回調函數隊列和失敗回調隊列和then方法
this._fulfilledQueues = []
this._rejectedQueues = []
複製代碼
2.then方法
將
then (onFulfilled, onRejected) {
const { _value, _status } = this
switch (_status) {
// 當狀態爲pending時,將then方法回調函數加入執行隊列等待執行
case 'PENDING':
this._fulfilledQueues.push(onFulfilled)
this._rejectedQueues.push(onRejected)
break
// 當狀態已經改變時,當即執行對應的回調函數
case 'FULFILLED':
onFulfilled(_value)
break
case 'REJECTED':
onRejected(_value)
break
}
// 返回一個新的Promise對象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
})
}
複製代碼
3.then方法規則完善
// 添加then方法
then (onFulfilled, onRejected) {
const { _value, _status } = this
// 返回一個新的Promise對象
return new MyPromise((onFulfilledNext, onRejectedNext) => {
// 封裝一個成功時執行的函數
let fulfilled = value => {
try {
if (typeof onFulfilled!=='function') {
onFulfilledNext(value)
} else {
let res = onFulfilled(value);
if (res instanceof MyPromise) {
// 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 若是函數執行出錯,新的Promise對象的狀態爲失敗
onRejectedNext(err)
}
}
// 封裝一個失敗時執行的函數
let rejected = error => {
try {
if (typeof onRejected!=='function') {
onRejectedNext(error)
} else {
let res = onRejected(error);
if (res instanceof MyPromise) {
// 若是當前回調函數返回MyPromise對象,必須等待其狀態改變後在執行下一個回調
res.then(onFulfilledNext, onRejectedNext)
} else {
//不然會將返回結果直接做爲參數,傳入下一個then的回調函數,並當即執行下一個then的回調函數
onFulfilledNext(res)
}
}
} catch (err) {
// 若是函數執行出錯,新的Promise對象的狀態爲失敗
onRejectedNext(err)
}
}
switch (_status) {
// 當狀態爲pending時,將then方法回調函數加入執行隊列等待執行
case 'PENDING':
this._fulfilledQueues.push(fulfilled)
this._rejectedQueues.push(rejected)
break
// 當狀態已經改變時,當即執行對應的回調函數
case 'FULFILLED':
fulfilled(_value)
break
case 'REJECTED':
rejected(_value)
break
}
})
}
複製代碼
4.當 resolve 或 reject 方法執行時,咱們依次提取成功或失敗任務隊列當中的函數開始執行,並清空隊列,從而實現 then 方法的屢次調用
// 添加resovle時執行的函數
_resolve (val) {
if (this._status !== PENDING) return
// 依次執行成功隊列中的函數,並清空隊列
const run = () => {
this._status = FULFILLED
this._value = val
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(val)
}
}
// 爲了支持同步的Promise,這裏採用異步調用
setTimeout(() => run(), 0)
}
// 添加reject時執行的函數
_reject (err) {
if (this._status !== PENDING) return
// 依次執行失敗隊列中的函數,並清空隊列
const run = () => {
this._status = REJECTED
this._value = err
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(err)
}
}
// 爲了支持同步的Promise,這裏採用異步調用
setTimeout(run, 0)
}
複製代碼
5.當 resolve 方法傳入的參數爲一個 Promise 對象時,則該 Promise 對象狀態決定當前 Promise 對象的狀態
// 添加resovle時執行的函數
_resolve (val) {
const run = () => {
if (this._status !== PENDING) return
this._status = FULFILLED
// 依次執行成功隊列中的函數,並清空隊列
const runFulfilled = (value) => {
let cb;
while (cb = this._fulfilledQueues.shift()) {
cb(value)
}
}
// 依次執行失敗隊列中的函數,並清空隊列
const runRejected = (error) => {
let cb;
while (cb = this._rejectedQueues.shift()) {
cb(error)
}
}
/* 若是resolve的參數爲Promise對象,則必須等待該Promise對象狀態改變後,
當前Promsie的狀態纔會改變,且狀態取決於參數Promsie對象的狀態
*/
if (val instanceof MyPromise) {
val.then(value => {
this._value = value
runFulfilled(value)
}, err => {
this._value = err
runRejected(err)
})
} else {
this._value = val
runFulfilled(val)
}
}
// 爲了支持同步的Promise,這裏採用異步調用
setTimeout(run, 0)
}
複製代碼
6.catch方法
// 添加catch方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
複製代碼
1.靜態 resolve 方法
// 添加靜態resolve方法
static resolve (value) {
// 若是參數是MyPromise實例,直接返回這個實例
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
複製代碼
2.靜態 reject 方法
// 添加靜態reject方法
static reject (value) {
return new MyPromise((resolve ,reject) => reject(value))
}
複製代碼
3.靜態 all 方法
// 添加靜態all方法
static all (list) {
return new MyPromise((resolve, reject) => {
/**
* 返回值的集合
*/
let values = []
let count = 0
for (let [i, p] of list.entries()) {
// 數組參數若是不是MyPromise實例,先調用MyPromise.resolve
this.resolve(p).then(res => {
values[i] = res
count++
// 全部狀態都變成fulfilled時返回的MyPromise狀態就變成fulfilled
if (count === list.length) resolve(values)
}, err => {
// 有一個被rejected時返回的MyPromise狀態就變成rejected
reject(err)
})
}
})
}
複製代碼
4.靜態 race 方法
// 添加靜態race方法
static race (list) {
return new MyPromise((resolve, reject) => {
for (let p of list) {
// 只要有一個實例率先改變狀態,新的MyPromise的狀態就跟着改變
this.resolve(p).then(res => {
resolve(res)
}, err => {
reject(err)
})
}
})
}
複製代碼
5.done方法
做用:無論以then方法或catch方法結尾,要是最後一個方法拋出錯誤,都有可能沒法捕捉到(由於 Promise 內部的錯誤不會冒泡到全局);處於回調鏈的尾端,保證拋出任何可能出現的錯誤 目前 Promise 上尚未 done,咱們能夠自定義一個
Promise.prototype.done = function (onFulfilled, onRejected) {
console.log('done')
this.then(onFulfilled, onRejected)
.catch((reason)=> {
// 拋出一個全局錯誤
setTimeout(() => {
throw reason
}, 0)
})
}
Promise.resolve('這是靜態方法的第一個 resolve 值').then(()=>{
return '這是靜態方法的第二個 resolve 值'
}).then(()=>{
throw('這是靜態方法的第一個 reject 值')
return '這是靜態方法的第二個 resolve 值'
}).done()
複製代碼
6.finally方法
做用:無論 Promise 對象最後狀態如何,都會執行的操做
與done方法的最大區別,它接受一個普通的回調函數做爲參數,該函數無論怎樣都必須執行
finally (cb) {
return this.then(
value => MyPromise.resolve(cb()).then(() => value),
reason => MyPromise.resolve(cb()).then(() => { throw reason })
);
};
複製代碼
7.完整代碼
請戳,源碼地址 歡迎 star!
1.Generator能夠理解爲一個狀態機,內部封裝了不少狀態,同時返回一個迭代器Iterator對象;
2.迭代器Iterator對象:定義標準方式產生一個有限或無限序列值,迭代器有next()對象;
3.屢次返回能夠被 next屢次調用,最大特色是能夠控制執行順序;
2.是一種特殊的函數
function* gen(x){
const y = yield x + 6;
return y;
}
// yield 若是用在另一個表達式中,要放在()裏面
// 像上面若是是在=右邊就不用加()
function* genOne(x){
const y = `這是第一個 yield 執行:${yield x + 1}`;
return y;
}
複製代碼
整個 Generator 函數就是一個封裝的異步任務,或者說是異步任務的容器。異步操做須要暫停的地方,都用 yield 語句註明
1.普通執行
const g = gen(1);
//執行 Generator 會返回一個Object,而不是像普通函數返回return 後面的值
g.next() // { value: 7, done: false }
//調用指針的 next 方法,會從函數的頭部或上一次停下來的地方開始執行,直到遇到下一個 yield 表達式或return語句暫停,也就是執行yield 這一行
// 執行完成會返回一個 Object,
// value 就是執行 yield 後面的值,done 表示函數是否執行完畢
g.next() // { value: undefined, done: true }
// 由於最後一行 return y 被執行完成,因此done 爲 true
複製代碼
2.next 方法傳參數
const g = gen(1);
g.next() // { value: 7, done: false }
g.next(2) // { value: 2, done: true }
// next 的參數是做爲上個階段異步任務的返回結果
3.嵌套執行
必須用到yield*關鍵字
複製代碼
function* genTwo(x){ yield* gen(1) yield* genOne(1) const y = 這是第 二個 yield 執行:${yield x + 2}
; return y; } const iterator=genTwo(1) // 由於 Generator 函數運行時生成的是一個 Iterator 對象,因此能夠直接使用 for...of 循環遍歷 for(let value of iterator) { console.log(value) }
複製代碼
相同點: 1.都能返回語句後面的那個表達式的值
2.均可以暫停函數執行 區別:
一個函數能夠有多個 yield,可是隻能有一個 return
yield 有位置記憶功能,return 沒有
拋出錯誤,能夠被try...catch...捕捉到
g.throw('這是拋出的一個錯誤');
// 這是拋出的一個錯誤
複製代碼
// 要求:函數valOne,valTwo,valThree 以此執行
function* someTask(){
try{
const valOne=yield 1
const valTwo=yield 2
const valThree=yield 3
}catch(e){
}
}
scheduler(someTask());
function scheduler(task) {
const taskObj = task.next(task.value);
console.log(taskObj)
// 若是Generator函數未結束,就繼續調用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}
複製代碼
原理圖
實現一個迭代器(Iterator)
// 源碼實現
function createIterator(items) {
var i = 0
return {
next: function() {
var done = (i >= items.length)
var value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
}
}
// 應用
const iterator = createIterator([1, 2, 3])
console.log(iterator.next()) // {value: 1, done: false}
console.log(iterator.next()) // {value: 2, done: false}
console.log(iterator.next()) // {value: 3, done: false}
console.log(iterator.next()) // {value: undefined, done: true}
複製代碼
實現可迭代(Iterable) 1.能夠經過 for...of...遍歷的對象,即原型鏈上有Symbol.iterator屬性;
2.Symbol.iterator:返回一個對象的無參函數,被返回對象符合迭代器協議; 3.for...of接受一個可迭代對象(Iterable),或者能強制轉換/包裝成一個可迭代對象的值(如’abc’),遍歷時,for...of會獲取可迭代對象的'Symbol.iterator',對該迭代器逐次調用next(),直到迭代器返回對象的done屬性爲true時,遍歷結束,不對該value處理;
const a = ['a', 'b', 'c', 'd', 'e']
for (let val of a) {
console.log(val)
}
// 'a' 'b' 'c' 'd' 'e'
// 等價於
const a = ["a", "b", "c", "d", "e"]
for (let val, ret, it = a[Symbol.iterator]();
(ret = it.next()) && !ret.done;
) {
let = ret.value
console.log(val)
}
// "a" "b" "c" "d" "e"
複製代碼
4.yield* 可返回一個 Iterable對象;
5.源碼改造
function createIterator(items) {
let i = 0
return {
next: function () {
let done = (i >= items.length)
let value = !done ? items[i++] : undefined
return {
done: done,
value: value
}
}
[Symbol.iterator]: function () {
return this
}
}
}
let iterator = createIterator([1, 2, 3])
...iterator // 1, 2, 3
複製代碼
1.for...of...接收可迭代對象,能強制轉換或包裝可迭代對象的值;
2.遍歷時,for...of會獲取可迭代對象的'Symbol.iterator',對該迭代器逐次調用next(),直到迭代器返回對象的done屬性爲true時,遍歷結束,不對該value處理;
3.因此能夠利用 for...of...封裝到原型鏈上.
Object.prototype[Symbol.iterator] = function* () {
for (const key in this) {
if (this.hasOwnProperty(key)) {
yield [key, this[key]]
}
}
}
複製代碼
1.async 函數返回的是一個 Promise 對象
在函數中 return 一個直接量,async 會把這個直接量經過 Promise.resolve() 封裝成 Promise 對象
async function testAsync() {
return "hello async";
}
const result = testAsync();
console.log(result); //Promise 對象
複製代碼
2.async和then
async返回一個Promise,因此能夠經過then獲取值
testAsync().then(v => {
console.log(v); // 輸出 hello async
});
複製代碼
因此async裏面的函數會立刻執行,這個就相似Generator的‘*’
1.await後面能夠是Promise對象或其餘表達式
function getSomething() {
return "something";
}
async function testAsync() {
return Promise.resolve("hello async");
}
async function test() {
const v1 = await getSomething();
const v2 = await testAsync();
console.log(v1, v2); //something 和 hello async
}
test();
複製代碼
2.await後面不是Promise對象,直接執行
3.await後面是Promise對象會阻塞後面的代碼,Promise 對象 resolve,而後獲得 resolve 的值,做爲 await 表達式的運算結果
4.因此這就是await必須用在async的緣由,async恰好返回一個Promise對象,能夠異步執行阻塞
1.主要是處理Promise的鏈式回調或函數的地獄回調 回到Generator中要求函數valOne,valTwo,valThree函數依次執行
function valOne(){}
function valTwo(){}
function valThree(){}
async ()=>{
await valOne()
await valTwo()
await valThree()
}
複製代碼
2.處理異常
try...catch...
或者await .catch()
1.async是內置執行器,Generator 函數的執行必須依靠執行器,無需手動執行next()
2.更廣的適用性。co模塊約定,yield命令後面只能是 Thunk 函數或 Promise 對象,而await後面能夠是任意表達式,都會返回一個Promise對象
// Thunk函數:是能將執行結果傳入回調函數,並將該回調函數返回的函數
function f(m) {
return m * 2;
}
f(x + 5);
// 等同於
var thunk = function () {
return x + 5;
};
function f(thunk) {
return thunk() * 2;
}
複製代碼
3.返回Promise,而Generator返回 Iterator
4.async 函數就是 Generator 函數的語法糖
async就至關於Generator的*,await至關於yield,用法有不少類似之處
實現執行器兩種方式:
回調函數(Thunk 函數)
Promise 對象
async function fn(args) {
// ...
}
// 等價於
function fn(args) {
return spawn(function* () {
// ...
});
}
function spawn(gen){
let g = gen();
function next(data){
let result = g.next(data);
if (result.done) return result.value;
result.value.then(function(data){
next(data);
});
}
next();
}
複製代碼
function spawn(genF) { //spawn函數就是自動執行器,跟簡單版的思路是同樣的,多了Promise和容錯處理
return new Promise(function(resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch(e) {
return reject(e);
}
if(next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(function(v) {
step(function() { return gen.next(v); });
}, function(e) {
step(function() { return gen.throw(e); });
});
}
step(function() { return gen.next(undefined); });
});
}
複製代碼
1.代碼對比:
場景:假定某個 DOM 元素上面,部署了一系列的動畫,前一個動畫結束,才能開始後一個。若是當中有一個動畫出錯,就再也不往下執行,返回上一個成功執行的動畫的返回值。
A.Promise
function chainAnimationsPromise(elem, animations) {
// 變量ret用來保存上一個動畫的返回值
let ret = null;
// 新建一個空的Promise
let p = Promise.resolve();
// 使用then方法,添加全部動畫
for(let anim of animations) {
p = p.then(function(val) {
ret = val;
return anim(elem);
});
}
// 返回一個部署了錯誤捕捉機制的Promise
return p.catch(function(e) {
/* 忽略錯誤,繼續執行 */
}).then(function() {
return ret;
});
}
複製代碼
B.Generator
function chainAnimationsGenerator(elem, animations) {
return spawn(function*() {
let ret = null;
try {
for(let anim of animations) {
ret = yield anim(elem);
}
} catch(e) {
/* 忽略錯誤,繼續執行 */
}
return ret;
});
}
複製代碼
C.async
async function chainAnimationsAsync(elem, animations) {
let ret = null;
try {
for(let anim of animations) {
ret = await anim(elem);
}
} catch(e) {
/* 忽略錯誤,繼續執行 */
}
return ret;
}
複製代碼
對比能夠看出 async...await...代碼更優雅
async 和 await 是在 Generator 的基礎上封裝了自執行函數和一些特性;
具體對比見沒個 API 的 PolyFill
來道頭條的面試
console.log('script start')
async function async1() {
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 end')
}
async1()
setTimeout(function() {
console.log('setTimeout')
}, 0)
new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})
console.log('script end')
// 舊版 Chrome 打印
// script start => async2 end => Promise => script end => promise1 => promise2 => async1 end => setTimeout
// 新版 Chrome 打印
// script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout
// 這裏面其餘的順序除了async1 end , promise1 , promise2 這幾個順序有點爭議,其餘應該沒有什麼問題
// 新版是由於V8 團隊將最新的規範進行了修改,await變得更快了,這道題細節分析再也不贅述,上面原理都有講到
複製代碼
原創碼字不易,歡迎 star!