ES6之generator,async,await

引言

在ES2017(ES8)標(biāo)準(zhǔn)引入async函數(shù)峰鄙,使得異步操作變得更加方便蜻拨,其實(shí)在ES6中基于Generator+Promise給嵌套的異步任務(wù)提供了一個(gè)非常便捷的解決方案剂公,這是異步任務(wù)非常典型的一種場(chǎng)景。有了生成器的函數(shù)執(zhí)行新模式的出現(xiàn)映砖,讓標(biāo)準(zhǔn)進(jìn)一步跟進(jìn)規(guī)范這一場(chǎng)景的解決方案真慢,這就是async函數(shù)的誕生,而實(shí)際上async函數(shù)就是Generator函數(shù)的語(yǔ)法糖榕吼。

async 是一個(gè)通過(guò)異步執(zhí)行并隱式返回 Promise 作為結(jié)果的函數(shù)(MDN)饿序。

generator

Generator 語(yǔ)法,需要了解function* 羹蚣、yield原探、next三個(gè)基本概念。
generator函數(shù)產(chǎn)生一個(gè)迭代器,迭代器對(duì)外暴露一個(gè)next方法咽弦,通過(guò)這個(gè)方法徒蟆,獲得迭代器的yield的值,同時(shí)獲得done型型,用來(lái)表示下一次next是不是能獲取到值段审。
1.function* 用來(lái)聲明一個(gè)函數(shù)是生成器函數(shù),它比普通的函數(shù)聲明多了一個(gè),的位置比較隨意可以挨著 function 關(guān)鍵字输莺,也可以挨著函數(shù)名

2.yield 產(chǎn)出的意思,這個(gè)關(guān)鍵字只能出現(xiàn)在生成器函數(shù)體內(nèi)裸诽,但是生成器中也可以沒(méi)有yield 關(guān)鍵字嫂用,函數(shù)遇到 yield 的時(shí)候會(huì)暫停,并把 yield 后面的表達(dá)式結(jié)果拋出去

3.next作用是將代碼的控制權(quán)交還給生成器函數(shù)

生成器的使用

代碼示例

function* fun(n) {
    let a = yield n * 2;
    let b = yield a + 5;
    let c = yield b / 2;
    return c;
}
let funGen = fun(12);
let resultObj1 = funGen.next();//{value: 24, done: false}
let resultObj2 = funGen.next(resultObj1.value);//{value: 29, done: false}
let resultObj3 = funGen.next(resultObj2.value);//{value: 14.5, done: false}
let resultObj4 = funGen.next(resultObj3.value);//{value: 14.5, done: true}
console.log(resultObj4.value); //14.5

過(guò)程分析

1.生成器就是在普通函數(shù)名稱(chēng)與function關(guān)鍵字之間的任意位置標(biāo)記一個(gè)“*”表示該函數(shù)是一個(gè)生成器丈冬。

2.生成器執(zhí)行會(huì)返回一個(gè)Generator對(duì)象嘱函,也可以視該對(duì)象為一個(gè)Iterator,因?yàn)樵搶?duì)象同樣可以被迭代埂蕊。

3.生成器執(zhí)行生成一個(gè)Generator對(duì)象的時(shí)候迭代器內(nèi)部代碼不會(huì)執(zhí)行往弓,而是需要通過(guò)對(duì)象調(diào)用next()方法才會(huì)執(zhí)行內(nèi)部代碼。

4.Generator對(duì)象調(diào)用next()方法基于yield關(guān)鍵字迭代執(zhí)行蓄氧,next()方法第一次執(zhí)行是從頭開(kāi)始執(zhí)行到第一個(gè)yield的右側(cè)代碼函似,yield左側(cè)代碼會(huì)等到下一個(gè)next()調(diào)用才會(huì)執(zhí)行。當(dāng)所有yield關(guān)鍵被迭代完成以后喉童,最后一個(gè)next()方法返回的對(duì)象中done屬性值為true撇寞,表示該Generator被迭代到最末尾處。

5.被yield截?cái)嗟谋磉_(dá)式除了作為阻斷代碼執(zhí)行的作用以外堂氯,yield關(guān)鍵字同時(shí)充當(dāng)了表達(dá)式右側(cè)代碼的return功能蔑担,將右側(cè)代碼執(zhí)行結(jié)果作為當(dāng)前next()方法的返回值;yield關(guān)鍵還充當(dāng)了左側(cè)代碼被next()調(diào)用時(shí)接收參數(shù)的功能咽白。(yield關(guān)鍵之應(yīng)該很容易理解啤握,它的功能就是截?cái)喑绦驁?zhí)行,并且通過(guò)返回值和接收參數(shù)的方式連接被截?cái)嗟某绦颍?/p>

6.yield作用的return特性其返回值最后會(huì)被next()方法返回的對(duì)象中的value屬性獲取晶框。

generator函數(shù)和普通函數(shù)對(duì)比.png

生成器 + Promise

使用生成器取代Promise的鏈?zhǔn)秸{(diào)用

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}

function *read(){
    let val1 = yield readFile('./data/number.txt');
    let val2 = yield readFile(val1);
    let val3 = yield readFile(val2);
    return val3;
};
let oG = read();
let {value, done} = oG.next();
value.then((val) => {
    let {value,done} = oG.next(val);
    value.then((val) => {
        let {value,done} = oG.next(val);
        value.then((val) => {
            console.log(val);
        });
    });
});

生成器遞歸委托:使用委托模式實(shí)現(xiàn)生成器自動(dòng)迭代排抬,使用遞歸消除next的重復(fù)調(diào)用

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}

function *read(){
    let val1 = yield readFile('./data/number.txt');
    let val2 = yield readFile(val1);
    let val3 = yield readFile(val2);
    return val3;
};

function Co(oIt){ //生成器迭代委托
    return new Promise((res,rej) =>{
        let next = (data) => {
            let {value, done} = oIt.next(data);
            if(done){
                res(value);//當(dāng)?shù)鞯竭_(dá)最末尾時(shí),將生成器的返回值傳遞給Promise的受理回調(diào)執(zhí)行回調(diào)任務(wù)
            }else{
                value.then((val) => {
                    next(val);//將上一個(gè)生成器返回值傳遞給下一個(gè)生成器的迭代方法next(這是個(gè)遞歸操作)
                },rej);//在生成器迭代過(guò)程中如果發(fā)生異常會(huì)調(diào)用rej處理
            }
        }
        next();//生成器第一次執(zhí)行不需要參數(shù)
    });
}

Co(read()).then((val) => {
    console.log(val);
},(val) => {
    console.log(val);
});

async+await

使用async函數(shù)如何改寫(xiě)上節(jié)中的示例:

let fs = require('fs');

function readFile(path){
    return new Promise((res,rej) => {
        fs.readFile(path, 'utf-8', (err,data) => {
            if(data){
                res(data);
            }else{
                rej(err);
            }
        });
    });
}
async function read(){
    let val1 = await readFile('./data/number.txt');
    let val2 = await readFile(val1);
    let val3 = await readFile(val2);
    return val3;
};
read().then((val) => {
    console.log(val); //99
})

從表面上看async將異步鏈?zhǔn)角短兹蝿?wù)完全轉(zhuǎn)化成了按照代碼編寫(xiě)的先后順序的同步執(zhí)行任務(wù)授段,這里所指代的同步任務(wù)是指由async+await控制的內(nèi)部異步任務(wù)畜埋,async函數(shù)本身是一個(gè)異步任務(wù),它執(zhí)行返回的是一個(gè)Promise對(duì)象用來(lái)處理異步鏈?zhǔn)饺蝿?wù)的最后回調(diào)處理畴蒲。
從async本身來(lái)說(shuō)就是將內(nèi)部代碼交給一個(gè)Promise作為excutor函數(shù)悠鞍,然后將return返回值交給回調(diào)函數(shù),Promise通過(guò)then注冊(cè)的回調(diào)任務(wù)作為微任務(wù)(異步)處理。簡(jiǎn)單的說(shuō)async函數(shù)返回一個(gè)Promise對(duì)象咖祭,并將返回值作為回調(diào)任務(wù)的參數(shù)掩宜,這里的底層實(shí)現(xiàn)可以直接參數(shù)Co理解,也可以稱(chēng)為內(nèi)置執(zhí)行器么翰,轉(zhuǎn)碼工具中的函數(shù)名稱(chēng)是asyncReadFile()牺汤。接著來(lái)看await在async函數(shù)內(nèi)的作用:


yield和await關(guān)鍵字的作用.png

async函數(shù)的使用

// 函數(shù)聲明
async function foo(){}
// 函數(shù)表達(dá)式
let foo = async function(){}
// 對(duì)象方法
let obj = {async foo(){}};
obj.foo().then(...);
// class的方法
class Storage{
    constructor(){
        this.cachePromise = caches.open('avatars');
    }
    async getAvatar(name){
        const cache = await this.cachePromise;
        return cache.match(`/avatars/${name}.jpg`);
    }
}
const storage = new Storage();
storage.getAvatar('jake').then(...);
//箭頭函數(shù)
const foo = async () => {};

參考資料

https://www.cnblogs.com/ZheOneAndOnly/p/11411971.html
https://blog.csdn.net/zjscy666/article/details/95365911?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
http://www.reibang.com/p/6055bd421ca4

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市浩嫌,隨后出現(xiàn)的幾起案子檐迟,更是在濱河造成了極大的恐慌,老刑警劉巖码耐,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件追迟,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡骚腥,警方通過(guò)查閱死者的電腦和手機(jī)敦间,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)束铭,“玉大人廓块,你說(shuō)我怎么就攤上這事∑跄” “怎么了带猴?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懈万。 經(jīng)常有香客問(wèn)我浓利,道長(zhǎng),這世上最難降的妖魔是什么钞速? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任贷掖,我火速辦了婚禮,結(jié)果婚禮上渴语,老公的妹妹穿的比我還像新娘苹威。我一直安慰自己,他們只是感情好驾凶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布牙甫。 她就那樣靜靜地躺著,像睡著了一般调违。 火紅的嫁衣襯著肌膚如雪窟哺。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天技肩,我揣著相機(jī)與錄音且轨,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛旋奢,可吹牛的內(nèi)容都是我干的泳挥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼至朗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屉符!你這毒婦竟也來(lái)了负蠕?” 一聲冷哼從身側(cè)響起偿乖,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筑凫,沒(méi)想到半個(gè)月后嫌变,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體吨艇,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年初澎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秸应。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虑凛。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碑宴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出桑谍,到底是詐尸還是另有隱情延柠,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布锣披,位于F島的核電站贞间,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雹仿。R本人自食惡果不足惜增热,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胧辽。 院中可真熱鬧峻仇,春花似錦、人聲如沸邑商。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)人断。三九已至吭从,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恶迈,已是汗流浹背涩金。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸭廷。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓枣抱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親辆床。 傳聞我的和親對(duì)象是個(gè)殘疾皇子佳晶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355