javascript generator生成器函數(shù)與asnyc/await語法糖
Generator 異步方案
相比于傳統(tǒng)回調(diào)函數(shù)的方式處理異步調(diào)用,Promise最大的優(yōu)勢就是可以鏈?zhǔn)秸{(diào)用解決回調(diào)嵌套的問題。但是這樣寫依然會(huì)有大量的回調(diào)函數(shù),雖然他們之間沒有嵌套,但是還是沒有達(dá)到傳統(tǒng)同步代碼的可讀性。如果以下面的方式寫異步代碼,它是很簡潔,也更容易閱讀的。
// 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提供了生成器函數(shù)(Generator Function)它與普通函數(shù)的語法差別在于,在function語句之后和函數(shù)名之前,有一個(gè)“*”作為生成器函數(shù)的標(biāo)示符。
在我們?nèi)フ{(diào)用生成器函數(shù)的時(shí)候他并不會(huì)立即去執(zhí)行這個(gè)函數(shù),而是會(huì)得到一個(gè)生成器對(duì)象,直到我們手動(dòng)調(diào)用對(duì)象的next 方法,函數(shù)體才會(huì)開始執(zhí)行,我們可以使用關(guān)鍵字yield去向外返回一個(gè)值,我們可以在next方法的返回值中去拿到這個(gè)值。另外再返回的屬性中還有一個(gè)done關(guān)鍵字來表示生成器是否執(zhí)行完了,
yield不會(huì)像return一樣去結(jié)束函數(shù)的執(zhí)行,只是暫停函數(shù)的執(zhí)行,直到外接下一次調(diào)用next方法時(shí)才會(huì)繼續(xù)從yield位置往下執(zhí)行
function * foo () { ? console.log('start') ?? ?yield 'foo' } const generator = foo() const result = generator.next()
調(diào)用next方法的時(shí)候傳入了參數(shù)的話,所傳入的參數(shù)會(huì)作為yield關(guān)鍵字的返回值
function * foo () { ? console.log('start') ?? ?// 我可以在這里接收next傳入的參數(shù) ?? ?const res = yield 'foo' ? console.log(res) // 這是我傳入的參數(shù) } const generator = foo() const result = generator.next('這是我傳入的參數(shù)') console.log(result) // { value: 'foo', done: false }
如果我們調(diào)用了生成器函數(shù)的throw方法,這個(gè)方法會(huì)給生成器函數(shù)內(nèi)部拋出一個(gè)異常
function * foo () { ? console.log('start') ? // 我可以在這里接收next傳入的參數(shù) ? try { ? ? const res = yield 'foo' ? ? console.log(res) // 這是我傳入的參數(shù) ? } catch (err) { ? ? console.log(err.message) // 拋出錯(cuò)誤 ? } } const generator = foo() const result = generator.next('這是我傳入的參數(shù)') console.log(result) generator.throw(new Error('拋出錯(cuò)誤'))
利用生成器函數(shù)和Promise來實(shí)現(xiàn)異步編程的體驗(yàn)
function ajax(url) { ? return new Promise((resove, reject) => { ? ? var xhr = new XMLHttpRequest() ? ? xhr.open('GET', url) ? ? // 新方法可以直接接受一個(gè)j對(duì)象 ? ? 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 => { ? 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) ? ? }) ? }) })
很明顯生成器的執(zhí)行器可以使用遞歸的方式去調(diào)用
const g = main() function handleResult(result) { if (result.done) return result.value.then(data => { handleResult(g.next(data)) }, err => { g.throw(err) }) } handleResult(g.next())
生成器函數(shù)的調(diào)用其實(shí)都是差不多的,所以我們可以寫一個(gè)比較通用的執(zhí)行器
function co(generator) { ? const g = generator() ? function handleResult(result) { ? ? if (result.done) return ? ? result.value.then(data => { ? ? ? handleResult(g.next(data)) ? ? }, err => { ? ? ? g.throw(err) ? ? }) ? } ? handleResult(g.next()) } co(main)
當(dāng)然這樣的執(zhí)行器在社區(qū)中已經(jīng)有一個(gè)比較完善的庫了co。這種co的方案在2015年之前是特別流行的,后來在出了async/await語法糖之后,這種方案相對(duì)來講就沒有那么普及了。使用generator這種方法最明顯的變化就是異步調(diào)用回歸到扁平化了
async/await
有了generator之后js異步編程基本上與同步代碼有類似的體驗(yàn)了,但是使用generator這種異步方案還需要自己手動(dòng)去寫一個(gè)執(zhí)行器函數(shù),會(huì)比較麻煩。在ES2017的版本中新增了一個(gè)叫做async的函數(shù),它同樣提供了這種扁平化的編程體驗(yàn),并且是語言層面的標(biāo)準(zhǔn)的異步編程語法。其實(shí)async函數(shù)就是生成器函數(shù)更方便的語法糖,所以語法上給generator函數(shù)是類似的。
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 函數(shù)返回一個(gè)Promise對(duì)象,更利于對(duì)整體代碼控制
promise.then(() => { ? console.log('all completed') }).catch(err => { ? console.log(err) })
到此這篇關(guān)于generator(生成器函數(shù))與asnyc/await的文章就介紹到這了,更多相關(guān)異步編程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用js控件div的滾動(dòng)條,讓它在內(nèi)容更新時(shí)自動(dòng)滾到底部的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄胘s控件div的滾動(dòng)條,讓它在內(nèi)容更新時(shí)自動(dòng)滾到底部的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-10-10JS中const對(duì)于復(fù)雜類型變量和普通類型變量的區(qū)別詳解
我們在開發(fā)的過程中一定常常發(fā)現(xiàn)const關(guān)鍵字定義的簡單類型變量不可以改變,但是你如果定義的是一個(gè)復(fù)雜類型變量(比如對(duì)象)的話對(duì)里面屬性的增刪改查是可以的,那這又是為什么呢,接下來就來和小編一起探討一下吧2023-11-11JavaScript輸入分鐘、秒倒計(jì)時(shí)技巧總結(jié)(附代碼)
這篇文章主要介紹了JavaScript輸入分鐘、秒倒計(jì)時(shí)的代碼實(shí)現(xiàn),通過css和js代碼展示了邏輯過程,具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。2017-08-08分享JavaScript與Java中MD5使用兩個(gè)例子
這篇文章主要為大家分享了JavaScript與Java中MD5使用兩個(gè)例子,2015-12-12詳解小程序設(shè)置緩存并且不覆蓋原有數(shù)據(jù)
這篇文章主要介紹了小程序設(shè)置緩存并且不覆蓋原有數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04利用JavaScript編寫一個(gè)花里胡哨的點(diǎn)擊按鈕
這篇文章主要介紹了如何利用HTML+CSS+JavaScript制作一個(gè)花里胡哨的點(diǎn)擊按鈕。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-03-03js 時(shí)間函數(shù)應(yīng)用加、減、比較、格式轉(zhuǎn)換的示例代碼
時(shí)間函數(shù)應(yīng)用加、減、比較、格式轉(zhuǎn)換等等,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下,希望對(duì)大家有所幫助2013-08-08