Generator 異步方案
相比于傳統回呼函式的方式處理異步呼叫,Promise最大的優勢就是可以鏈式呼叫解決回呼嵌套的問題,但是這樣寫依然會有大量的回呼函式,雖然他們之間沒有嵌套,但是還是沒有達到傳統同步代碼的可讀性,如果以下面的方式寫異步代碼,它是很簡潔,也更容易閱讀的,
// like sync mode
try{
const value1 = ajax('/api/url1')
console.log(value1)
const value2 = ajax('/api/url1')
console.log(value2)
const value3 = ajax('/api/url1')
console.log(value3)
const value4 = ajax('/api/url1')
console.log(value4)
const value5 = ajax('/api/url1')
console.log(value5)
}catch(err){
console.log(err)
}
在ES2015提供了生成器函式(Generator Function)它與普通函式的語法差別在于,在function陳述句之后和函式名之前,有一個“*”作為生成器函式的標示符,
在我們去呼叫生成器函式的時候他并不會立即去執行這個函式,而是會得到一個生成器物件,直到我們手動呼叫物件的next 方法,函式體才會開始執行,我們可以使用關鍵字yield去向外回傳一個值,我們可以在next方法的回傳值中去拿到這個值,另外再回傳的屬性中還有一個done關鍵字來表示生成器是否執行完了,
yield不會像return一樣去結束函式的執行,只是暫停函式的執行,直到外接下一次呼叫next方法時才會繼續從yield位置往下執行
function * foo () {
console.log('start')
yield 'foo'
}
const generator = foo()
const result = generator.next()
呼叫next方法的時候傳入了引數的話,所傳入的引數會作為yield關鍵字的回傳值
function * foo () {
console.log('start')
// 我可以在這里接收next傳入的引數
const res = yield 'foo'
console.log(res) // 這是我傳入的引數
}
const generator = foo()
const result = generator.next('這是我傳入的引數')
console.log(result) // { value: 'foo', done: false }
如果我們呼叫了生成器函式的throw方法,這個方法會給生成器函式內部拋出一個例外
function * foo () {
console.log('start')
// 我可以在這里接收next傳入的引數
try {
const res = yield 'foo'
console.log(res) // 這是我傳入的引數
} catch (err) {
console.log(err.message) // 拋出錯誤
}
}
const generator = foo()
const result = generator.next('這是我傳入的引數')
console.log(result)
generator.throw(new Error('拋出錯誤'))
利用生成器函式和Promise來實作異步編程的體驗
function ajax(url) {
return new Promise((resove, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
// 新方法可以直接接受一個j物件
xhr.responseType = 'json'
xhr.onload = function () {
if (this.status === 200) {
resove(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
function* main() {
const user1 = yield ajax('/json1.json')
console.log(user1)
const user2 = yield ajax('/json2.json')
console.log(user2)
const user3 = yield ajax('/json3.json')
console.log(user3)
}
const g = main()
const result = g.next()
result.value.then(data =https://www.cnblogs.com/kspf/archive/2022/11/01/> {
const result2 = g.next(data)
if (result2.done) return
result2.value.then(data2 => {
const result3 = g.next(data2)
if (result3.done) return
result3.value.then(data3 => {
g.next(data3)
})
})
})
很明顯生成器的執行器可以使用遞回的方式去呼叫
const g = main()
function handleResult(result) {
if (result.done) return
result.value.then(data =https://www.cnblogs.com/kspf/archive/2022/11/01/> {
handleResult(g.next(data))
}, err => {
g.throw(err)
})
}
handleResult(g.next())
生成器函式的呼叫其實都是差不多的,所以我們可以寫一個比較通用的執行器
function co(generator) {
const g = generator()
function handleResult(result) {
if (result.done) return
result.value.then(data =https://www.cnblogs.com/kspf/archive/2022/11/01/> {
handleResult(g.next(data))
}, err => {
g.throw(err)
})
}
handleResult(g.next())
}
co(main)
當然這樣的執行器在社區中已經有一個比較完善的庫了co,這種co的方案在2015年之前是特別流行的,后來在出了async/await語法糖之后,這種方案相對來講就沒有那么普及了,使用generator這種方法最明顯的變化就是異步呼叫回歸到扁平化了
async/await
有了generator之后js異步編程基本上與同步代碼有類似的體驗了,但是使用generator這種異步方案還需要自己手動去寫一個執行器函式,會比較麻煩,在ES2017的版本中新增了一個叫做async的函式,它同樣提供了這種扁平化的編程體驗,并且是語言層面的標準的異步編程語法,其實async函式就是生成器函式更方便的語法糖,所以語法上給generator函式是類似的,
async function main() {
try {
const user1 = await ajax('/json1.json')
console.log(user1)
const user2 = await ajax('/json2.json')
console.log(user2)
const user3 = await ajax('/json3.json')
console.log(user3)
} catch (error) {
console.log(error)
}
}
main()
async 函式回傳一個Promise物件,更利于對整體代碼控制
promise.then(() => {
console.log('all completed')
}).catch(err => {
console.log(err)
})
原文地址: https://kspf.xyz/archives/21
更多內容微信公眾號搜索充饑的泡飯
小程式搜一搜開水泡飯的博客
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/525053.html
標籤:其他
上一篇:9 個美觀大氣的后臺管理系統
下一篇:JS 和 CSS 小結
