Js Promise async await Generator


前言

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

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

一次回調(diào)當(dāng)然簡(jiǎn)單,如果是在這次請(qǐng)求完成后需要立即發(fā)起下一次請(qǐng)求呢?例如需要請(qǐng)求request10次,必須在上次請(qǐng)求完成后才能進(jìn)行下一次請(qǐng)求,來(lái)看看 回調(diào)地獄 是怎么樣的

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

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


首先 Promise

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

雖然還是很長(zhǎng),但是至少嵌套很少了,可讀性也比之前更高


再來(lái)看看 async
切記,async必須和Promise配合使用

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

代碼是不是更加簡(jiǎn)短了?而且看起來(lái)和同步一樣,事實(shí)上,這就是使用同步的方式寫異步代碼,這代碼也是同步執(zhí)行的


最后看看 generator

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

以上是異步編程的ES6解決方案,接下來(lái)讓我們把這3種方式都詳細(xì)了解下


一.Promise

關(guān)于Promise的一些原型,函數(shù),請(qǐng)移步 官方鏈接
Promise的中文名,也就是承諾,保證,
大家可以將Promise理解為JS的一個(gè)承諾,也就是對(duì)異步操作的一個(gè)承諾,咱先不管異步操作是否能夠執(zhí)行成功,使用Promise的所有異步操作,JS已經(jīng)承諾處理了,咱就通過Promise的狀態(tài)來(lái)知曉異步操作的執(zhí)行結(jié)果。
一個(gè) Promise有以下幾種狀態(tài):

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

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


二.async / await

想詳細(xì)了解更多,請(qǐng)移步官方文檔
async關(guān)鍵字用來(lái)定義一個(gè)function,用來(lái)標(biāo)識(shí)此函數(shù)是一個(gè)異步函數(shù)
切記 切記 切記, await 關(guān)鍵字僅僅在 async function中有效。如果在 async function函數(shù)體外使用 await ,你只會(huì)得到一個(gè)語(yǔ)法錯(cuò)誤SyntaxError

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

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

    }
    async data => {

    }
    class Test {
        async show() {
            
        }
    }

無(wú)論是普通的函數(shù),Lambda表達(dá)式,或是ES6的class,該關(guān)鍵字都是放置在函數(shù)的聲明之前
在調(diào)用聲明了async的函數(shù)時(shí),會(huì)返回一個(gè)Promise對(duì)象
await關(guān)鍵字則是放置在異步操作的調(diào)用之前,await會(huì)使得async函數(shù)在執(zhí)行到異步操作時(shí)暫停代碼執(zhí)行,直到異步操作返回的Promise狀態(tài)更改為 fulfilledrejected,此時(shí)代碼會(huì)繼續(xù)執(zhí)行,并自動(dòng)解析Promise返回的值,無(wú)論是成功還是失敗,都會(huì)解析到await關(guān)鍵字前面定義接收的參數(shù),請(qǐng)看例子:

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()

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


三 . generator

generator(生成器)是ES6標(biāo)準(zhǔn)引入的新的數(shù)據(jù)類型,使用方式是在函數(shù)名前加上*,generator和普通的函數(shù)差不多,但是可以返回多次。
嗯,所有的函數(shù)都有默認(rèn)的返回值,如果沒有明確定義,那就會(huì)返回undefined,generator也不例外!
generator使用yield關(guān)鍵字來(lái)中途返回值,請(qǐng)看案例

    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的值,此時(shí)num為調(diào)用a函數(shù)時(shí)傳入的值 1,yield返回了num+1,所以第一行打印的對(duì)象 value值是 2
第二次打印的是sum值,也就是第一個(gè)yield關(guān)鍵字前面接收的值,此值是下面result.next(2)傳入的 ,next函數(shù)傳入的參數(shù),會(huì)賦值到相應(yīng)的yield關(guān)鍵字左邊接收的那個(gè)變量,在上方案例,也就是sum變量
第三次輸出的是a函數(shù)中第二個(gè)yield返回的值,此時(shí)sum為第一次next(2)傳入的2,所以此次返回的值是2+2,所以值也就是 4
第四次輸出的是最后一個(gè)next(),但是上方generator并沒有相應(yīng)的yield返回,所以此時(shí)的valueundefined
yield返回的值是一個(gè)對(duì)象,其中有donevalue兩個(gè)屬性,

  • done 表示該generator是否執(zhí)行完畢,當(dāng)沒有yield返回時(shí),done的值為true,也就是代表當(dāng)前generator執(zhí)行完畢
  • value表示此次yield關(guān)鍵字右方表達(dá)式返回的值,當(dāng)沒有yield時(shí),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);
        
    }


事實(shí)證明generator是實(shí)現(xiàn)了迭代器的接口的!
嗯,關(guān)于generator的實(shí)際應(yīng)用場(chǎng)景,我是沒有遇見的,不過聽說 async/await是generator的語(yǔ)法糖??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 含義 async函數(shù)是Generator函數(shù)的語(yǔ)法糖,它使得異步操作變得更加方便。 寫成async函數(shù),就是下面這...
    oWSQo閱讀 2,043評(píng)論 0 2
  • 一、Javascript實(shí)現(xiàn)異步編程的過程以及原理 1、為什么要用Javascript異步編程 眾所周知,Java...
    Ebony_7c03閱讀 937評(píng)論 0 2
  • 弄懂js異步 講異步之前,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,886評(píng)論 0 5
  • 一. Callback (回調(diào)函數(shù)) 1.定義:把函數(shù)當(dāng)作變量傳到另一個(gè)函數(shù)里,傳進(jìn)去之后執(zhí)行甚至返回等待之后的...
    hutn閱讀 1,618評(píng)論 0 2
  • async 函數(shù) 含義 ES2017 標(biāo)準(zhǔn)引入了 async 函數(shù),使得異步操作變得更加方便。 async 函數(shù)是...
    huilegezai閱讀 1,320評(píng)論 0 6

友情鏈接更多精彩內(nèi)容