最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS中Generator函數(shù)與async函數(shù)用法介紹

 更新時間:2023年06月07日 08:28:48   作者:剪刀石頭布啊  
javascript中經(jīng)常會用到異步編程,在ES6之后我們使用的?Generator函數(shù)、async函數(shù)、promise都是我們異步編程的一大助力,這里我們主要講解Generator、async函數(shù),并且簡介他們之間的一些聯(lián)系,本篇文章會帶著一些簡易案例,方便大家理解使用

Generator函數(shù)

Generator 函數(shù)看起來像指針函數(shù),實際上跟我們的普通函數(shù)差不多,有兩個特征

1、function關(guān)鍵字與函數(shù)名之間有一個星號

2、函數(shù)體內(nèi)部使用yield表達式,定義不同的內(nèi)部狀態(tài)(yield在英語里的意思就是“產(chǎn)出”),這里有暫停、等待執(zhí)行的意思

Generator函數(shù) 運行后跟普通函數(shù)一樣,只不過碰到 yield 關(guān)鍵字會暫停,需要等待調(diào)用next 之后才會繼續(xù)往后執(zhí)行,直到遇到下一個 yieldreturn

yield 作為表達式一項時,需要使用 () 括起來,否則會報錯,例如: 2 * (yield x)

next 標識著 generator 函數(shù)開始往后執(zhí)行語句,默認執(zhí)行到 下一個 yield 語句,并返回 yield 后的內(nèi)容value 中,沒有遇到 return或者函數(shù)結(jié)束,done參數(shù)為false,否則done參數(shù)為 true

return 標識著函數(shù)的結(jié)束,這里也不例外,只不過結(jié)束時返回的內(nèi)容也可以被 next 返回; 另外,不寫 return 則與return undefined類似

可以參考下面案例理解

function* generator() {
    yield 1
    yield 2
    yield 3
    yield 4
    return 5  // { value: 5, done: true },再往后 next 就和不寫一樣了
    // 不寫return,則與return undefined 一樣 返回 { value: undefined, done: true }
}
//獲取 generator 遍歷器
let g = generator()
let next = null
do {
    next = g.next() //開始往后執(zhí)行到下一個 yield 語句,并返回
    console.log(next)
}while(next?.done === false) //執(zhí)行到 return 或 函數(shù)結(jié)束 則停止打印
next = g.next()
console.log(next)
// { value: 1, done: false }
// { value: 2, done: false }
// { value: 3, done: false }
// { value: 4, done: false }
// 有return 5 打印 { value: 5, done: true }, 沒有則打印 { value: undefined, done: true }

如果在 class 中怎么表示呢,里面是構(gòu)造函數(shù)的包裝,寫法不一樣, 只需要在前面加上一個即可

class A {
    //前面加上 * 即可
    * generator() {
        yield 1
        yield 2
    }
}
let a = new A()
let g = a.generator()
let next = g.next()
console.log(next)
next = g.next()
console.log(next)

generator 與 Iterator遍歷器

從上面打印就可以看出,generator函數(shù),實際上返回的是一個 Iterator遍歷器,因此我們亦可以通過遍歷器的手段來執(zhí)行 generator遍歷器

下面使用 for ... of 來遍歷一下我們上面的 generator遍歷器

//從上面就可以看出,generator函數(shù) 返回了一個 Iterator遍歷器對象
//我們遍歷一次試試
let g = generator()
for (let item of g) {
    console.log(item)
}
//結(jié)果打印 1 2 3 4

看其結(jié)果發(fā)現(xiàn)沒有打印最后的return,了解遍歷器就會知道,遍歷器遇到 donetrue時會直接結(jié)束(作為遍歷器使用的話,不要在 return 語句 放遍歷內(nèi)容)

我們再對比一下看看我們的 generator遍歷器是否真的是 Iteraotr遍歷器,發(fā)現(xiàn)一模一樣

//g[Symbol.iterator]() === g //遍歷器屬性一致,返回為 true,不信打印遍歷器屬性比較試試

next

前面介紹了,generator遍歷器調(diào)用 next(),會繼續(xù)執(zhí)行到下一個 yield語句

此外,next 可以傳參,我們傳遞的參數(shù)會作為 上一個 yield 返回的結(jié)果,沒有傳參,默認返回都是 undefined

注意yield 返回的結(jié)果不是該表達式后面的內(nèi)容,且 yield 作為表達式一項時,需要使用 () 括起來,否則會報錯

下面看一下案例就知道結(jié)果了(過程已經(jīng)標出)

//包含表達式時,yield 語句需要用括號括起來,否則會報錯
function* generator() {
    let num1 = yield 1
    console.log('num1', num1)
    let num2 = num1 * (yield 2)
    console.log('num2', num2)
    yield 3
    yield 4
}
//next傳入的值,作為上一個 yield 的返回值使用
let g = generator()
let next = g.next() //此時執(zhí)行到 yield 1,并包裝返回 yield 1 執(zhí)行的結(jié)果
console.log(next)  //{ value: 1, done: false }
//傳入 2, 當做上一個語句的 yield 1 的返回值(不傳接收到的值均為undefined)
//即:返回 2 賦值給 num,然后執(zhí)行到 (yield 2),并包裝返回 (yield 2) 的結(jié)果
next = g.next(2) //執(zhí)行完畢后,打印:num1 2
console.log(next) //{ value: 2, done: false }
//傳入 3. 當做上一個語句的 (yield 2) 的返回值(不傳接收到的值均為undefined)
//即:num2 = num1 * 3, 然后執(zhí)行到 yield 3,并包裝返回 yield 3 的結(jié)果
next = g.next(3)  //執(zhí)行完畢后,打?。簄um2 6
console.log(next) //{ value: 3, done: false }

由上述可以可以看到 next 傳參 挺好用的,除了上述,甚至可以使用傳遞的參數(shù),用于跳出死循環(huán)......

throw

這里的throw為通過 generator遍歷器拋出的異常,其異常會拋出到 generator 函數(shù)里面,因此需要注意拋出異常的位置,才能更好地配合try...catch使用

//throw()
// 相當于在 generator 里面拋出了一個異常
function* generator() {
    try {
        yield 1
    }catch(err) {
        console.log(err)
    }
    yield 2
    yield 3
    yield 4
}
let g = generator()
let next = g.next()
console.log(next)
//執(zhí)行完畢 yield 1后,隨后在 yield 1 ~ yield 2 之間拋出異常,因此,try...catch 包裹主 yield 1 才行
let t = g.throw('啦啦啦') // 拋出異常并執(zhí)行到下一個 yield,如果沒處理錯誤,程序異常
console.log(t)
next = g.next()
console.log(next)

return

指定 return 會理立即結(jié)束 generator 函數(shù),并返回 { value: undefined, done: true },上面有介紹

function* generator() {
    yield 1
    yield 2
    yield 3
    yield 4
}
let g = generator()
next = g.next()
next = g.return() //直接返回 { value: undefined, done: true },如果傳參則返回return傳遞的參數(shù)
console.log(next) //會直接結(jié)束

另外,當存在 try...finally 代碼塊時,并且執(zhí)行到 try 里面時return ,會仍然執(zhí)行finally 里面的語句,且會執(zhí)行到finally里面yield語句,finally執(zhí)行完畢,才是真正結(jié)束,并且return帶入的參數(shù),會應用到 finally語句的末尾

function* generator() {
    yield 1
    try {
        //執(zhí)行到這里,外面 return 的話,也需要走完 finally 才會結(jié)束
        yield 2
        yield 3
    }finally {
        yield 7
        yield 8
    }
    yield 4
}
let g1 = generator()
next = g1.next() // { value: 1, done: false }
next = g1.next() // { value: 2, done: false }
next = g1.return(100) // { value: 7, done: false },return 后返回下一個,為finally中的 yield 7,這里順道返回一個值測試一下最后的返回值
next = g1.next() // { value: 8, done: false }
next = g1.next() // { value: 100, done: true }
next = g1.next() // { value: undefined, done: true }

yield*

yield*看著像個指針,其指向一個 generator遍歷器,會自動展開,不多說

//yield* 會展開 generator 函數(shù)
function* gen1() {
    yield 1
    yield 2
}
function* gen2() {
    yield 0
    yield* gen1()
}
//相當于下面展開的語句,可以看出yield語句總是一個一個執(zhí)行的,即使嵌套也不會一次執(zhí)行一堆
function* gen3() {
    yield 0
    yield 1
    yield 2
}

generator應用

generator應用很多,這里簡單介紹兩種,算是小試牛刀

正常切換開關(guān),需要狀態(tài),這里通過 generator 遍歷器,避免了新增開關(guān)狀態(tài)(不適用于多方控制,例如:需要與后臺遠程同步狀態(tài))

//切換狀態(tài)
function* toggleSwitch() {
    while(true) {
        console.log('開燈')
        yield 1
        console.log('關(guān)燈')
        yield 0
    }
}
//是不是切換很簡單了
let t = toggleSwitch()
let next = t.next() //開燈
console.log(next)
next = t.next() //關(guān)燈
console.log(next)

流程化管理,外部不用關(guān)心內(nèi)部(例如:加工軟件,不同工種對于自己的操作工序,完成后只需要點擊一下 next 即可)

function* order() {
    yield '收到訂單'
    yield '加工'
    yield '送檢'
    yield '質(zhì)檢'
    yield '發(fā)貨'
    yield '完成訂單'
}
let o = order()
let next = o.next() //收到訂單
next = o.next() //加工
next = o.next() //送檢
next = o.next() //質(zhì)檢
next = o.next() //發(fā)貨
next = o.next() //完成訂單

async、await函數(shù)

async、await,我們平時用的比較多,這里簡單介紹一下

async、await 就是根據(jù) generator函數(shù)改造而成,其改進了 generator函數(shù)作為普通函數(shù)痛點,其加入了執(zhí)行器,能像普通函數(shù)一樣直接執(zhí)行,更加方便,且語義更加清晰,結(jié)果返回一個 Promise(可以看出使用Promise包裝而成)

簡而言之async 聲明一個異步函數(shù),await 等待執(zhí)行,當await語句執(zhí)行完畢后,才會執(zhí)行后面語句(出現(xiàn)錯誤直接拋出錯誤結(jié)束,不往后執(zhí)行),并且如果沒有 await 的存在,async函數(shù),跟一個普通的同步函數(shù)執(zhí)行順序沒有什么區(qū)別

ps: 關(guān)于 promise 前面有介紹,await執(zhí)行的過程與其一樣,實際上會馬上執(zhí)行任務隊列下一個任務,前面的任務執(zhí)行完畢后,才會執(zhí)行到 await 后面的語句

簡單做一個使用案例,

async function a() {
    await promise1...
    await promise2...
    // 如果不return自己內(nèi)容,返回的則是 undefind
    return 1
}
function cc() {
    a().then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err)
    })
}

你可能會想,如果第一個 promise 出錯了怎么辦,第二個會執(zhí)行么?

答案:不會,第一個出錯后,會拋出一個異常,此時會結(jié)束解釋器的執(zhí)行,直接反饋錯誤到外部

下面改進上一個案例

function a1() {
    return Promise.reject('出錯了 ')
}
function a2() {
    return Promise.resolve('成功了 ')
}
async function a() {
    await a1()
    console.log(11111111111)
    await a2()
    // 如果不return自己內(nèi)容,返回的則是 undefind
    return 1
}
function cc() {
    a().then(res => {
        console.log(res)
    }).catch(err => {
        console.log(err)
    })
}
//結(jié)果是沒有執(zhí)行打印 11111111111,且執(zhí)行到了 .catch 中

下面我們模擬傳遞多個圖片的案例,可以看出 async、await 應用多么方便

function uploadAImage(fileUrl) {
    return new Promise(function(resolve, reject) {
        setTimeout(() => {
            let res = fileUrl + '成功了'
            console.log(res)
            resolve(res)
        }, 500);
    })
}
async function uploadImages() {
    //準備多個圖片url
    let fileUrl = ['url1', 'url2', 'url3']
    for (url of fileUrl) {
        try {
            await uploadAImage(url)
        }catch(err) {
            console.log(err)
            //實際上更復雜,成功的下次就不需要在上傳了
            return Promise.reject('存在上傳失敗的內(nèi)容') 
        }
    }
    return '都成功了'
}
uploadImages().then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

async、await 函數(shù)使用簡單就先介紹到這里了,后面會對 generator 進行對比,是怎么改造了的

generator 模仿 async 自動執(zhí)行

前面說了 async函數(shù) 是通過 generator函數(shù) 改造而來的,里面添加了執(zhí)行器,我們自己嘗試一下做一個簡易的generator執(zhí)行器

這里是一個async 的默認執(zhí)行案例

//使用 generator + 執(zhí)行器,簡單翻譯一下 async、await
async function asyncDefaultFunction() {
    let res = await new Promise((resolve) => {
        setTimeout(() => {
            resolve(1)
        }, 1000);
        // setTimeout(() => {
        //     reject('啦啦啦')
        // }, 1000);
    })
    return res
}
asyncDefaultFunction().then(res => {
    console.log(res)
})

我們使用 generator 改造成 async 自動執(zhí)行的模式

//使用 async 之后,實際上我們將返回函數(shù)包裝一下即可,并且將 yield 就是 await
function asyncFunction() {
    return co(function* () {
        //async 函數(shù)里面的內(nèi)容,將 yield 與 await 替換即可
        let res = yield new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(1)
            }, 1000);
            // setTimeout(() => {
            //     reject('啦啦啦')
            // }, 1000);
        })
        return res
    })
}
//編寫一個自動執(zhí)行 generator 的 co 函數(shù)
function co(genFunc) {
    return new Promise(function (resolve, reject) {
        const g = genFunc() //獲取generator,準備執(zhí)行
        function nextFunc(value) {
            let next;
            try {
                next = g.next(value)
            }catch(err) {
                return reject(err)
            }
            if (next.done) {
                return resolve(next.value)
            }
            //為什么要promise包裝,語句可能為同步函數(shù),可能為異步函數(shù),木事保證正確執(zhí)行
            //如果為 promise 直接原封不動返回,如果為普通對象則包裝,具體見promise
            Promise.resolve(next.value).then(function(res) {
                nextFunc(res)
            }, function(err) {
                reject(err)
            })
        }
        nextFunc(undefined)
    })
}
asyncFunction().then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})

這樣就實現(xiàn)了一個簡易的 generator 自動執(zhí)行器

同時我們也發(fā)現(xiàn)了,async 與我們普通函數(shù)相比,加劇了負擔,為了優(yōu)化性能,我們平時如果沒有用到異步函數(shù)(或者連 await 都用不到的),將多余的 async 去掉吧,這樣某種程度上能夠避免性能浪費,尤其是到了循環(huán)語句

最后

看到了這里,相信我們也能學習到不少東西,可以想一下,其他語言是不是也是類似這樣呢,遇到了相似的情況我們是不是也豁然開朗了呢,很多語言是互通的,學習就是積累的過程,希望我們此次能有所收獲!

以上就是JS中Generator函數(shù)與async函數(shù)用法介紹的詳細內(nèi)容,更多關(guān)于JS Generator函數(shù)與async函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JavaScript編寫實現(xiàn)飛機大戰(zhàn)

    JavaScript編寫實現(xiàn)飛機大戰(zhàn)

    這篇文章主要為大家詳細介紹了JavaScript編寫實現(xiàn)飛機大戰(zhàn),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Javascript必知必會(四)js類型轉(zhuǎn)換

    Javascript必知必會(四)js類型轉(zhuǎn)換

    這篇文章主要介紹了Javascript必知必會(四)js類型轉(zhuǎn)換 的相關(guān)資料,非常不錯具有參考借鑒價值,需要的朋友可以參考下
    2016-06-06
  • security.js實現(xiàn)的RSA加密功能示例

    security.js實現(xiàn)的RSA加密功能示例

    這篇文章主要介紹了security.js實現(xiàn)的RSA加密功能,結(jié)合實例形式分析了基于security.js進行RSA加密的相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06
  • require導入module.exports 或 exports導出的使用方法

    require導入module.exports 或 exports導出的使用方法

    module.exports用于導出整個模塊的內(nèi)容,可以通過賦值給 module.exports 導出一個對象、函數(shù)或值,導出的內(nèi)容可以被其他模塊通過require 導入,本文給大家介紹require導入module.exports 或 exports導出的使用,感興趣的朋友一起看看吧
    2023-11-11
  • js中開關(guān)變量使用實例

    js中開關(guān)變量使用實例

    本文主要分享了js中開關(guān)變量使用實例的代碼,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 用JavaScript實現(xiàn)動畫效果的方法

    用JavaScript實現(xiàn)動畫效果的方法

    大家在使用Dreamweaver中的時間線功能或以做出很有趣的動畫效果,Dreamweaver會自動為用戶生成特定的程序代碼,大家有沒有想過動畫的實現(xiàn)原理呢?
    2013-07-07
  • js實現(xiàn)彈出框的拖拽效果實例代碼詳解

    js實現(xiàn)彈出框的拖拽效果實例代碼詳解

    本文通過實例代碼給大家介紹了js實現(xiàn)彈出框的拖拽效果,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-04-04
  • 利用JS如何計算字符串所占字節(jié)數(shù)示例代碼

    利用JS如何計算字符串所占字節(jié)數(shù)示例代碼

    因為最近項目有個需求要用js計算一串字符串寫入到localStorage里所占的內(nèi)存,所以便有了這篇文章,下面這篇文章主要給大家介紹了關(guān)于利用JS如何計算字符串所占字節(jié)數(shù)的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09
  • js獲取某月的最后一天日期的簡單實例

    js獲取某月的最后一天日期的簡單實例

    js獲取某月的最后一天日期的簡單實例,需要的朋友可以參考一下
    2013-06-06
  • JavaScript中Promise的簡單使用及其原理詳解

    JavaScript中Promise的簡單使用及其原理詳解

    Promise是ES6最重要的特性之一,今天小編就來帶大家一起系統(tǒng)且細致的研究一下Promise的用法以及原理,感興趣的小伙伴可以學習一下哦
    2023-03-03

最新評論

贺州市| 禹州市| 昂仁县| 江津市| 库车县| 宿迁市| 东至县| 宁陕县| 和田市| 西藏| 丰顺县| 岫岩| 高陵县| 浦县| 黄龙县| 石嘴山市| 龙海市| 通渭县| 遵化市| 平南县| 绵竹市| 温泉县| 松阳县| 确山县| 栖霞市| 樟树市| 新宁县| 广昌县| 罗定市| 黎城县| 电白县| 都匀市| 承德市| 固阳县| 威信县| 华阴市| 东海县| 祁门县| 芜湖市| 怀仁县| 宁海县|