理解ES6的異步方案Promise/Generator/async

異步

  • 不連續(xù)的執(zhí)行纯赎,就叫做異步。相應(yīng)地励稳,連續(xù)的執(zhí)行就叫做同步佃乘。
  • 通常異步是處理一些耗時的操作。

回想在ES6沒出現(xiàn)之前驹尼,需要異步處理事件的時候大多數(shù)情況下就是回調(diào)函數(shù)趣避,可能還是是一層一層的callback嵌套,代碼混亂邏輯不清晰新翎,如果還有錯誤處理程帕,那代碼就更加冗雜讓人不想去讀第二遍住练!

回調(diào)

通過函數(shù)參數(shù)傳遞到其他代碼里的,某一塊可以執(zhí)行的引用愁拭。

容易引發(fā)的問題

1.信任問題
2.同步or異步 不確定讲逛?

Promise對象

所謂Promise,簡單說就是一個容器岭埠,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果盏混。

  • Promise 是一個對象,從它可以獲取異步操作的消息惜论。Promise對象代表一個異步操作许赃,有三種狀態(tài):pending(進(jìn)行中)、fulfilled(已成功)和rejected(已失敼堇唷)混聊。
構(gòu)建Promise對象
var promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 異步操作成功 */){
    resolve(value);
  } 
  else {
    reject(error);
  }
});

promise的構(gòu)造函數(shù)傳入一個回調(diào),該回調(diào)接受兩個回調(diào)函數(shù)resolve和reject蹦掐。
resolve函數(shù)的作用是技羔,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時調(diào)用卧抗,并將異步操作的結(jié)果藤滥,作為參數(shù)傳遞出去;reject函數(shù)的作用是社裆,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected)拙绊,在異步操作失敗時調(diào)用,并將異步操作報出的錯誤泳秀,作為參數(shù)傳遞出去标沪。

Promise實例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)嗜傅。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

Promise 提供then方法加載回調(diào)函數(shù)金句,catch方法捕捉執(zhí)行過程中拋出的錯誤。

Promise.resovle方法吕嘀,可以將不是Promise對象作為參數(shù)违寞,返回一個Promise對象。
有兩種情形:
1.假設(shè)傳入的參數(shù)沒有一個.then方法偶房,那么這個返回的Promise對象變成了resolve狀態(tài)趁曼,其resolve的值就是這個對象本身。
2.假設(shè)傳入的參數(shù)帶有一個then方法(稱為thenable對象), 那么將這個對象的類型變?yōu)镻romise,其then方法變成Promise.prototype.then方法棕洋。

不管是then還是catch方法調(diào)用挡闰,都返回一個新的promise對象;其實then方法的調(diào)用,只不過是注冊了完成態(tài)和失敗態(tài)下的回調(diào)函數(shù)而已摄悯。這些回調(diào)函數(shù)組成一個回調(diào)隊列赞季,處理resolve的值。

Q 鏈?zhǔn)秸{(diào)用的then和非鏈?zhǔn)秸{(diào)用的有什么區(qū)別射众?
var promise1 = new Promise(function(resolve){
    resolve(1);
});
var thenPromise = promise1.then(function(value){
    console.log(value);
});
var catchPromise = thenPromise.catch(function(error){
    console.log(error);
});
console.log(promise1 !== thenPromise); // true
console.log(thenPromise !== catchPromise); //true

情況1

var promise1 = new Promise(function(resolve){
    resolve(1);
});
promise1.then(function(value){
    return value * 2;
});
promise1.then(function(value){
    return value * 2;
});
promise1.then(function(value){
    console.log("1"+value);
});

情況2

var promise1 = new Promise(function(resolve){
    resolve(2);
});
promise1.then(function(value){
    return value * 2;
}).then(function(value){
    return value * 2;
}).then(function(value){
    console.log("1"+value);
});

Generator 函數(shù)

整個 Generator 函數(shù)就是一個異步任務(wù)的容器碟摆。

Generator 函數(shù)除了狀態(tài)機(jī),還是一個遍歷器對象生成函數(shù)叨橱。返回的遍歷器對象典蜕,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個狀態(tài)。

  • Generator 函數(shù)是一個普通函數(shù)罗洗,但是有兩個特征愉舔。一是,function關(guān)鍵字與函數(shù)名之間有一個星號伙菜;二是轩缤,函數(shù)體內(nèi)部使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)贩绕。

  • 調(diào)用 Generator 函數(shù)火的,返回一個遍歷器對象,代表 Generator 函數(shù)的內(nèi)部指針淑倾。每次調(diào)用遍歷器對象的next方法馏鹤,內(nèi)部指針就從函數(shù)頭部或上一次停下來的地方開始執(zhí)行,直到遇到下一個yield表達(dá)式(或return語句)為止娇哆,會返回一個有著value和done兩個屬性的對象湃累,value屬性表示當(dāng)前的內(nèi)部狀態(tài)的值,是yield表達(dá)式后面那個表達(dá)式的值碍讨;done屬性是一個布爾值治力,表示是否遍歷結(jié)束。

function* gen(x) {
  var y = yield x + 2;
  return y;
}

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }
  • Generator 函數(shù)的一個重要實際意義就是用來處理異步操作勃黍,改寫回調(diào)函數(shù)宵统。
  • Generator 函數(shù)可以暫停執(zhí)行和恢復(fù)執(zhí)行,這是它能封裝異步任務(wù)的根本原因覆获。
  • 除此之外榜田,它還有兩個特性,使它可以作為異步編程的完整解決方案:函數(shù)體內(nèi)外的數(shù)據(jù)交換和錯誤處理機(jī)制锻梳。
function* asyncJob(){

// ...其他代碼

varf=yieldreadFile(fileA);

// ...其他代碼

}

上面代碼的函數(shù)asyncJob是一個協(xié)程,它的奧妙就在其中的yield命令净捅。它表示執(zhí)行到此處疑枯,執(zhí)行權(quán)將交給其他協(xié)程。
協(xié)程遇到y(tǒng)ield命令就暫停蛔六,等到執(zhí)行權(quán)返回荆永,再從暫停的地方繼續(xù)往后執(zhí)行废亭。
它的最大優(yōu)點,就是代碼的寫法非常像同步操作具钥。

async函數(shù)

async函數(shù)就是將 Generator 函數(shù)的星號(*)替換成async豆村,將yield替換成await
async告訴這個函數(shù)里面有await

async function getTitle(url) { 
  let response = await fetch(url); 
  let html = await response.text(); 
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
  • async函數(shù)返回一個 Promise 對象。
  • async函數(shù)內(nèi)部return語句返回的值骂删,會成為then方法回調(diào)函數(shù)的參數(shù)掌动。async函數(shù)內(nèi)部拋出錯誤,會導(dǎo)致返回的 Promise 對象變?yōu)閞eject狀態(tài)宁玫。拋出的錯誤對象會被catch方法回調(diào)函數(shù)接收到粗恢。
  • 如果await后面的異步操作出錯,那么等同于async函數(shù)返回的 Promise 對象被reject欧瘪。拋出的錯誤對象會被catch方法回調(diào)函數(shù)接收到眷射。
await拿到的東西就是promise resolve之后的值

async 函數(shù)的實現(xiàn)原理,就是將 Generator 函數(shù)和自動執(zhí)行器佛掖,包裝在一個函數(shù)里妖碉。

function spawn(genF) {
    return new Promise(function(resolve, reject) {
        var gen = genF();
        function step(nextF) {
            try {
                var 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); });
    });
}

比較

一個異步展示動畫的程序

// Promise寫法
function chainAnimationPromise(elem, animations) {
    var ret = null;
    var p = Promise.resolve();

    for (var anim of animations) {
        p = p.then(function(val) {
            ret = val;
            return anim(elem);
        });
    }

    return p.catch(function(e) {

    }).then(function() {
        return ret;
    });
}
// Generator寫法
function chainAnimationPromise(elem, animations) {
    return spawn(function* () {
        var ret = null;
        try {
            for (var anim of animations) {
                ret = yield anim(elem);
            }
        } catch (error) {
            
        }
        return ret;
    });
}
// async寫法
async function chainAnimationPromise(elem, animations) {
    var ret = null;
    try {
        for(var anim of animations) {
            ret = await anim(elem);
        }
    } catch (error) {
        
    }
    return ret;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市芥被,隨后出現(xiàn)的幾起案子欧宜,更是在濱河造成了極大的恐慌,老刑警劉巖撕彤,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鱼鸠,死亡現(xiàn)場離奇詭異,居然都是意外死亡羹铅,警方通過查閱死者的電腦和手機(jī)蚀狰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來职员,“玉大人麻蹋,你說我怎么就攤上這事『盖校” “怎么了扮授?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長专肪。 經(jīng)常有香客問我刹勃,道長,這世上最難降的妖魔是什么嚎尤? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任荔仁,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乏梁。我一直安慰自己次洼,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布遇骑。 她就那樣靜靜地躺著卖毁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪落萎。 梳的紋絲不亂的頭發(fā)上亥啦,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音模暗,去河邊找鬼禁悠。 笑死,一個胖子當(dāng)著我的面吹牛兑宇,可吹牛的內(nèi)容都是我干的碍侦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼隶糕,長吁一口氣:“原來是場噩夢啊……” “哼瓷产!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枚驻,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤濒旦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后再登,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體尔邓,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年锉矢,在試婚紗的時候發(fā)現(xiàn)自己被綠了梯嗽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,566評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡沽损,死狀恐怖灯节,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绵估,我是刑警寧澤炎疆,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站国裳,受9級特大地震影響形入,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缝左,卻給世界環(huán)境...
    茶點故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一唯笙、第九天 我趴在偏房一處隱蔽的房頂上張望螟蒸。 院中可真熱鬧,春花似錦崩掘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至英妓,卻和暖如春挽放,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蔓纠。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工辑畦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腿倚。 一個月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓纯出,卻偏偏與公主長得像,于是被迫代替她去往敵國和親敷燎。 傳聞我的和親對象是個殘疾皇子暂筝,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,440評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的硬贯,如果沒有異步編程焕襟,根本...
    呼呼哥閱讀 7,301評論 5 22
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎(chǔ)知識-event-loop饭豹。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,706評論 0 5
  • Promiese 簡單說就是一個容器鸵赖,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果,語法上說拄衰,Pr...
    雨飛飛雨閱讀 3,352評論 0 19
  • 簡單介紹下這幾個的關(guān)系為方便起見 用以下代碼為例簡單介紹下這幾個東西的關(guān)系它褪, async 在函數(shù)聲明前使用asyn...
    _我和你一樣閱讀 21,215評論 1 24
  • javascript的運行機(jī)制是單線程處理镐确,即只有上一個任務(wù)完成后包吝,才會執(zhí)行下一個任務(wù),這種機(jī)制也被稱為“同步”源葫。...
    我是xy閱讀 3,876評論 1 6