看一道面試必備的基礎(chǔ)題:異步實現(xiàn)一個 sleep 函數(shù)
點擊上方 三分鐘學前端,關(guān)注公眾號
一、JS異步解決方案的發(fā)展歷程以及優(yōu)缺點
1. 回調(diào)函數(shù)(callback)
setTimeout(() => {
// callback 函數(shù)體
}, 1000)
缺點:回調(diào)地獄,不能用 try catch 捕獲錯誤,不能 return
回調(diào)地獄的根本問題在于:
缺乏順序性:回調(diào)地獄導致的調(diào)試困難,和大腦的思維方式不符 嵌套函數(shù)存在耦合性,一旦有所改動,就會牽一發(fā)而動全身,即(控制反轉(zhuǎn)) 嵌套函數(shù)過多的多話,很難處理錯誤
ajax('XXX1', () => {
// callback 函數(shù)體
ajax('XXX2', () => {
// callback 函數(shù)體
ajax('XXX3', () => {
// callback 函數(shù)體
})
})
})
優(yōu)點:解決了同步的問題(只要有一個任務(wù)耗時很長,后面的任務(wù)都必須排隊等著,會拖延整個程序的執(zhí)行。)
2. Promise
Promise就是為了解決callback的問題而產(chǎn)生的。
Promise 實現(xiàn)了鏈式調(diào)用,也就是說每次 then 后返回的都是一個全新 Promise,如果我們在 then 中 return ,return 的結(jié)果會被 Promise.resolve() 包裝
優(yōu)點:解決了回調(diào)地獄的問題
ajax('XXX1')
.then(res => {
// 操作邏輯
return ajax('XXX2')
}).then(res => {
// 操作邏輯
return ajax('XXX3')
}).then(res => {
// 操作邏輯
})
缺點:無法取消 Promise ,錯誤需要通過回調(diào)函數(shù)來捕獲
3. Generator
特點:可以控制函數(shù)的執(zhí)行,可以配合 co 函數(shù)庫使用
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
4. Async/await
async、await 是異步的終極解決方案
優(yōu)點是:代碼清晰,不用像 Promise 寫一大堆 then 鏈,處理了回調(diào)地獄的問題
缺點:await 將異步代碼改造成同步代碼,如果多個異步操作沒有依賴性而使用 await 會導致性能上的降低。
async function test() {
// 以下代碼沒有依賴性的話,完全可以使用 Promise.all 的方式
// 如果有依賴性的話,其實就是解決回調(diào)地獄的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
}
下面來看一個使用 await 的例子:
let a = 0
let b = async () => {
a = a + await 10
console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1
對于以上代碼你可能會有疑惑,讓我來解釋下原因
首先函數(shù) b先執(zhí)行,在執(zhí)行到await 10之前變量a還是 0,因為await內(nèi)部實現(xiàn)了generator,generator會保留堆棧中東西,所以這時候a = 0被保存了下來因為 await是異步操作,后來的表達式不返回Promise的話,就會包裝成Promise.reslove(返回值),然后會去執(zhí)行函數(shù)外的同步代碼同步代碼執(zhí)行完畢后開始執(zhí)行異步代碼,將保存下來的值拿出來使用,這時候 a = 0 + 10
上述解釋中提到了 await 內(nèi)部實現(xiàn)了 generator,其實 await 就是 generator 加上 Promise的語法糖,且內(nèi)部實現(xiàn)了自動執(zhí)行 generator。如果你熟悉 co 的話,其實自己就可以實現(xiàn)這樣的語法糖。
二、4 種方式實現(xiàn)一個 sleep 函數(shù)
1. Promise
//Promise
const sleep = time => {
return new Promise(resolve => setTimeout(resolve,time))
}
sleep(1000).then(()=>{
console.log(1)
})
2. Generator
//Generator
function* sleepGenerator(time) {
yield new Promise(function(resolve,reject){
setTimeout(resolve,time);
})
}
sleepGenerator(1000).next().value.then(()=>{console.log(1)})
3. async await
//async
function sleep(time) {
return new Promise(resolve => setTimeout(resolve,time))
}
async function output() {
let out = await sleep(1000);
console.log(1);
return out;
}
output();
4. ES5
//ES5
function sleep(callback,time) {
if(typeof callback === 'function')
setTimeout(callback,time)
}
function output(){
console.log(1);
}
sleep(output,1000);來自:https://github.com/sisterAn/blog
