Js Promise async await Generator


前言

Promise async generatorES6之后才被提出來的需五,他們都能夠用來解決以前JS異步調(diào)用產(chǎn)生的一系列問題煮剧,例如大名鼎鼎的回調(diào)地獄!!!
什么是回調(diào)地獄?
在以前js中评肆,我們是無法知曉一個異步操作是否執(zhí)行完成恶复,為了在異步操作完成后執(zhí)行特定的代碼嘱腥,我們需要傳入回調(diào)函數(shù)畏鼓,請看下面的栗子:
這是一個簡單的例子,在請求完成后(可以理解為異步操作)執(zhí)行特定的代碼

    //我們需要在請求完成后輸出請求完成诈豌,請看回調(diào)法
    function show(params) {
        request('這是請求參數(shù)', () => {
            console.log('請求完成')
        })
    }
    /**
     * 模擬發(fā)起一個http請求
     * @param {object} data 請求的參數(shù)
     * @param {function} callBack 回調(diào)函數(shù)
     */
    function request(data, callBack) {
        //下面的定時器模擬請求時間
        setTimeout(data => {
            callBack(data);
        }, 3000);
    }
    show()

一次回調(diào)當然簡單仆救,如果是在這次請求完成后需要立即發(fā)起下一次請求呢?例如需要請求request10次矫渔,必須在上次請求完成后才能進行下一次請求彤蔽,來看看 回調(diào)地獄 是怎么樣的

    //我們需要在請求完成后輸出請求完成,請看回調(diào)法
    function show(params) {
        request('這是請求參數(shù)', () => {
            console.log('請求完成1次')
            request('這是請求參數(shù)', () => {
                console.log('請求完成2次')
                request('這是請求參數(shù)', () => {
                    console.log('請求完成3次')
                    request('這是請求參數(shù)', () => {
                        console.log('請求完成4次')
                        request('這是請求參數(shù)', () => {
                            console.log('請求完成5次')
                            //這才第五次.....
                        })
                    })
                })
            })
        })
    }
    /**
     * 模擬發(fā)起一個http請求
     * @param {object} data 請求的參數(shù)
     * @param {function} callBack 回調(diào)函數(shù)
     */
    function request(data, callBack) {
        //下面的定時器模擬請求時間
        setTimeout(data => {
            callBack(data);
        },1000);
    }
    show()

這才第5次回調(diào)庙洼,但是代碼的可讀性已經(jīng)極差了顿痪!
讓我們先看看 Promise async generator怎么解決這個問題镊辕,后面再說其使用方式


首先 Promise

   //我們需要在請求完成后輸出請求完成,請看回調(diào)法
   function show(params) {
       request('這是請求參數(shù)').then(
           resolve => {
               console.log('請求完成1次');
               return request('這是請求參數(shù)')
           }
       ).then(
           resolve => {
               console.log('請求完成2次');
               return request('這是請求參數(shù)')
           }
       ).then(
           resolve => {
               console.log('請求完成3次');
               return request('這是請求參數(shù)')
           }
       ).then(
           resolve => {
               console.log('請求完成4次');
               return request('這是請求參數(shù)')
           }
       ).then(
           resolve => {
               console.log('請求完成5次');
               return request('這是請求參數(shù)')
           }
       )
   }
   /**
    * 模擬發(fā)起一個http請求
    * @param {object} data 請求的參數(shù)
    * @param {function} callBack 回調(diào)函數(shù)
    */
   function request(data) {
       return new Promise(
           resolve => {
               //下面的定時器模擬請求時間
               setTimeout(data => {
                   resolve(data)
               }, 1000);
           }
       )
   }
   show()

雖然還是很長蚁袭,但是至少嵌套很少了征懈,可讀性也比之前更高


再來看看 async
切記,async必須和Promise配合使用

    //我們需要在請求完成后輸出請求完成揩悄,請看回調(diào)法
    async function show(params) {
        let result = await request('這是請求參數(shù)')
        console.log('請求完成1次');
        result = await request('這是請求參數(shù)')
        console.log('請求完成2次');
        result = await request('這是請求參數(shù)')
        console.log('請求完成3次');
        result = await request('這是請求參數(shù)')
        console.log('請求完成4次');
        result = await request('這是請求參數(shù)')
        console.log('請求完成5次');
    }
    /**
     * 模擬發(fā)起一個http請求
     * @param {object} data 請求的參數(shù)
     * @param {function} callBack 回調(diào)函數(shù)
     */
    function request(data) {
        return new Promise(
            resolve => {
                //下面的定時器模擬請求時間
                setTimeout(data => {
                    resolve(data)
                }, 1000);
            }
        )
    }
    show()

代碼是不是更加簡短了卖哎?而且看起來和同步一樣,事實上删性,這就是使用同步的方式寫異步代碼亏娜,這代碼也是同步執(zhí)行的


最后看看 generator

    //我們需要在請求完成后輸出請求完成,請看回調(diào)法
    function* show() {
        let a1 = yield request('請求參數(shù)', () => {
            console.log('這是第1次調(diào)用');
        });
        let a2 = yield request('請求參數(shù)', () => {
            console.log('這是第2次調(diào)用');
        });
        let a3 = yield request('請求參數(shù)', () => {
            console.log('這是第3次調(diào)用');
        });
        let a4 = yield request('請求參數(shù)', () => {
            console.log('這是第4次調(diào)用');
        });
        let a5 = yield request('請求參數(shù)', () => {
            console.log('這是第5次調(diào)用');
        });
    }
    /**
     * 模擬發(fā)起一個http請求
     * @param {object} data 請求的參數(shù)
     * @param {function} callBack 回調(diào)函數(shù)
     */
    function request(data, callBack) {
        //下面的定時器模擬請求時間
        setTimeout(() => {
            callBack(data)
        }, 1000);
    }
    let a = show()
    a.next();
    a.next();
    a.next();
    a.next();
    a.next();

以上是異步編程的ES6解決方案镇匀,接下來讓我們把這3種方式都詳細了解下


一.Promise

關于Promise的一些原型照藻,函數(shù),請移步 官方鏈接
Promise的中文名汗侵,也就是承諾幸缕,保證,
大家可以將Promise理解為JS的一個承諾晰韵,也就是對異步操作的一個承諾发乔,咱先不管異步操作是否能夠執(zhí)行成功,使用Promise的所有異步操作雪猪,JS已經(jīng)承諾處理了栏尚,咱就通過Promise的狀態(tài)來知曉異步操作的執(zhí)行結果。
一個 Promise有以下幾種狀態(tài):

  • pending: 初始狀態(tài)只恨,既不是成功译仗,也不是失敗狀態(tài)。
  • fulfilled: 表示著操作完成官觅,狀態(tài)成功纵菌。
  • rejected: 意味著操作失敗。
    pending 狀態(tài)的 Promise 對象可能會變?yōu)閒ulfilled 狀態(tài)并傳遞一個值給相應的狀態(tài)處理方法休涤,也可能變?yōu)槭顟B(tài)(rejected)并傳遞失敗信息咱圆。當其中任一種情況出現(xiàn)時,Promise 對象的 then 方法綁定的處理方法(handlers )就會被調(diào)用
    上文提到Promise的原型中的函數(shù)then功氨,then可以接收2個參數(shù)(resolve [,reject])
    第一個參數(shù)resolve 是對成功的一個處理序苏,類型為Function。你可以在其中做 一些異步成功后的操作
    第二個參數(shù)reject是對失敗的一個處理捷凄,類型為Function忱详。你可以在其中做 一些異步失敗后的操作
    一般情況下then我們只會傳一個參數(shù),也就是默認的成功處理跺涤,失敗處理會使用 catch函數(shù)
    catch函數(shù)只有一個參數(shù),也就是代表失敗的reject
    來看看then catch的使用案例
    function show(params) {
        //正常的請求成功操作
        request('這是請求參數(shù)').then(
            resolve => {
                console.log(resolve);
            }
        )
        //在then里面同時做成功和失敗的操作
        request('這是請求參數(shù)').then(
            resolve => {
                //這兒是成功
                console.log(resolve);
            },
            reject => {
                //這兒是失敗
                console.log(reject);
            }
        )
        //正常的請求失敗操作
        request('這是請求參數(shù)').catch(
            reject => {
                console.log(reject);
            }
        )
    }
    /**
     * 模擬發(fā)起一個http請求
     * @param {object} data 請求的參數(shù)
     * @param {function} callBack 回調(diào)函數(shù)
     */
    function request(data) {
        return new Promise(
            (resolve, reject) => {
                //下面的定時器模擬請求時間
                setTimeout(data => {
                    // resolve('請求成功')
                    reject('請求失敗')
                }, 1000);
            }
        )
    }
    show()

使用 Promise 能夠使你的異步操作變得更加優(yōu)雅匈睁,可讀性也比較高管钳,同時,Promise在ES6的各大插件中也使用的相當廣泛软舌,所以掌握 Promise是非常有必要的


二.async / await

想詳細了解更多,請移步官方文檔
async關鍵字用來定義一個function牛曹,用來標識此函數(shù)是一個異步函數(shù)
切記 切記 切記佛点, await 關鍵字僅僅在 async function中有效。如果在 async function函數(shù)體外使用 await 黎比,你只會得到一個語法錯誤SyntaxError

async關鍵字放在函數(shù)的聲明之前超营,例如:

    async function test() {
    }
    async () => {

    }
    async data => {

    }
    class Test {
        async show() {
            
        }
    }

無論是普通的函數(shù),Lambda表達式阅虫,或是ES6的class演闭,該關鍵字都是放置在函數(shù)的聲明之前
在調(diào)用聲明了async的函數(shù)時,會返回一個Promise對象
await關鍵字則是放置在異步操作的調(diào)用之前颓帝,await會使得async函數(shù)在執(zhí)行到異步操作時暫停代碼執(zhí)行米碰,直到異步操作返回的Promise狀態(tài)更改為 fulfilledrejected,此時代碼會繼續(xù)執(zhí)行,并自動解析Promise返回的值购城,無論是成功還是失敗吕座,都會解析到await關鍵字前面定義接收的參數(shù),請看例子:

class Test {
    /**
     * 線程休眠
     * @param {number} timer 休眠毫秒數(shù)
     */
    Sleep(timer) {
        return new Promise(
            resolve => {
                setTimeout(() => {
                    resolve(timer)
                }, timer);
            }
        )
    }
}
let T = new Test();
async function show() {
    console.log('第一次');
    await T.Sleep(1000)
    console.log('第二次');
}
show()

上面的實例調(diào)用show函數(shù)瘪板,會立即打印出 第一次,延時1000毫秒后吴趴,會打印出 第二次
原理嘛,就是模仿Java的線程休眠函數(shù)Sleep
在打印了 第一次 后侮攀,會調(diào)用T的Sleep函數(shù)锣枝,Sleep函數(shù)返回了一個Promise,在定時器執(zhí)行完畢后調(diào)用Promiseresolve函數(shù),會將Promise 的狀態(tài)更改為 fulfilled兰英,此時await檢測到Promise的狀態(tài)更改撇叁,繼續(xù)執(zhí)行代碼,輸出 第二次


三 . generator

generator(生成器)是ES6標準引入的新的數(shù)據(jù)類型,使用方式是在函數(shù)名前加上*,generator和普通的函數(shù)差不多箭昵,但是可以返回多次税朴。
嗯,所有的函數(shù)都有默認的返回值家制,如果沒有明確定義正林,那就會返回undefined,generator也不例外!
generator使用yield關鍵字來中途返回值颤殴,請看案例

    function* a(num) {
        let sum = yield num + 1
        console.log(sum);//2 此處是next(r.value)傳入的值
        let sum2 = yield sum + 2
    }
    let result = a(1);
    let r = result.next()
    console.log(r);//此處返回第一次yield的值 2
    console.log(result.next(2));//此處返回第二次yield的值 4
    console.log(result.next());//此處并沒有第三次yield


第一次輸出的是第一次yield的值觅廓,此時num為調(diào)用a函數(shù)時傳入的值 1,yield返回了num+1,所以第一行打印的對象 value值是 2
第二次打印的是sum值涵但,也就是第一個yield關鍵字前面接收的值杈绸,此值是下面result.next(2)傳入的 帖蔓,next函數(shù)傳入的參數(shù),會賦值到相應的yield關鍵字左邊接收的那個變量瞳脓,在上方案例塑娇,也就是sum變量
第三次輸出的是a函數(shù)中第二個yield返回的值,此時sum為第一次next(2)傳入的2,所以此次返回的值是2+2劫侧,所以值也就是 4
第四次輸出的是最后一個next()埋酬,但是上方generator并沒有相應的yield返回,所以此時的valueundefined
yield返回的值是一個對象烧栋,其中有donevalue兩個屬性写妥,

  • done 表示該generator是否執(zhí)行完畢,當沒有yield返回時审姓,done的值為true,也就是代表當前generator執(zhí)行完畢
  • value表示此次yield關鍵字右方表達式返回的值,當沒有yield時,valueundefined

generator是支持迭代器操作的珍特,例:

      function* a(num) {
        let sum = yield num + 1
        console.log(sum);//2 此處是next(r.value)傳入的值
        let sum2 = yield sum + 2
    }
    let result = a(1);
    for (const key of result) {
        console.log(key);
        
    }


事實證明generator是實現(xiàn)了迭代器的接口的!
嗯,關于generator的實際應用場景,我是沒有遇見的魔吐,不過聽說 async/await是generator的語法糖扎筒??

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末画畅,一起剝皮案震驚了整個濱河市砸琅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌轴踱,老刑警劉巖症脂,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異淫僻,居然都是意外死亡诱篷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門雳灵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來棕所,“玉大人,你說我怎么就攤上這事悯辙×帐。” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵躲撰,是天一觀的道長针贬。 經(jīng)常有香客問我,道長拢蛋,這世上最難降的妖魔是什么桦他? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮谆棱,結果婚禮上快压,老公的妹妹穿的比我還像新娘圆仔。我一直安慰自己,他們只是感情好蔫劣,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布坪郭。 她就那樣靜靜地躺著,像睡著了一般脉幢。 火紅的嫁衣襯著肌膚如雪截粗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天鸵隧,我揣著相機與錄音,去河邊找鬼意推。 笑死豆瘫,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的菊值。 我是一名探鬼主播外驱,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腻窒!你這毒婦竟也來了昵宇?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤儿子,失蹤者是張志新(化名)和其女友劉穎瓦哎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柔逼,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡蒋譬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了愉适。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片犯助。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖维咸,靈堂內(nèi)的尸體忽然破棺而出剂买,到底是詐尸還是另有隱情,我是刑警寧澤癌蓖,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布腊嗡,位于F島的核電站疙剑,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜焙贷,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦两残、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至着逐,卻和暖如春崔赌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背耸别。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工健芭, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秀姐。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓慈迈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親省有。 傳聞我的和親對象是個殘疾皇子痒留,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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

  • 含義 async函數(shù)是Generator函數(shù)的語法糖,它使得異步操作變得更加方便蠢沿。 寫成async函數(shù)伸头,就是下面這...
    oWSQo閱讀 1,991評論 0 2
  • 一、Javascript實現(xiàn)異步編程的過程以及原理 1舷蟀、為什么要用Javascript異步編程 眾所周知恤磷,Java...
    Ebony_7c03閱讀 860評論 0 2
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎知識-event-loop野宜。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,710評論 0 5
  • 一. Callback (回調(diào)函數(shù)) 1.定義:把函數(shù)當作變量傳到另一個函數(shù)里仿粹,傳進去之后執(zhí)行甚至返回等待之后的...
    hutn閱讀 1,525評論 0 2
  • async 函數(shù) 含義 ES2017 標準引入了 async 函數(shù),使得異步操作變得更加方便原茅。 async 函數(shù)是...
    huilegezai閱讀 1,259評論 0 6