天天看點

異步程式設計-Generator & asyncGenerator 基本用法Generator 與 Promise結合調用Generator的執行器async 和 await 文法糖

文章目錄

  • Generator 基本用法
  • Generator 與 Promise結合調用
  • Generator的執行器
  • async 和 await 文法糖

Generator 基本用法

  • 在普通函數前面加上 * ,表示Generator函數
  • 在函數内,使用 yield關鍵詞 暫停函數執行。可以向外抛出相關值,也可以接受函數外的值
  • 在函數外,使用 next()方法 啟動Generator函數。遇到 yield關鍵詞 時,可擷取其值。也可以傳遞參數到函數内
  • 函數外可以抛出異常入函數内,函數内可使用 try…catch 捕獲
console.log(`------------------------------\n----Generator基本用法\n------------------------------`)

// * 表示Generator函數
function* gener() {
    try {
        console.log("cernerator function")
        
        //遇到 yield關鍵詞,函數執行暫停。有傳回值,則向外抛出含有傳回值的對象
        //同時,可以接受函數外的傳值
        let outerData = yield "inner data"
        console.log(outerData) //outer data 
    } catch (error) {
        console.log(error)
    }
}

//調用Generator函數,隻是傳回Generator對象,并不是執行其代碼
let generator = gener()
console.log(generator) // gener {<suspended>}

//調用next()方法,才是啟動執行Generator函數的内部代碼
//可以接受Generator函數内部的傳回值
//傳回對象中的 done 是指函數是否執行完成
let innerData = generator.next()
console.log(innerData) //{value: "inner data", done: false}

//再次啟動Generator函數,可以傳遞值
generator.next("outer data")

//手動抛個異常
generator.throw(new Error("outer error"))
           

Generator 與 Promise結合調用

* 可以依次調用多個接口

* 将Promise的異步操作,通過Generator組裝為近似同步的操作

(users.json)

[
    {
        "name": "asd",
        "age": 32
    },
    {
        "name": "bbb",
        "age": 12
    }
]
           

(classes.json)

[
    {
        "className": "A1班",
        "level": "三年級"
    },
    {
        "className": "A5班",
        "level": "二年級"
    }
]
           

(js)

console.log(`------------------------------\n----Generator 與 Promise結合調用\n------------------------------`)

//請求函數
const ajax = function (url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open("GET", url)
        xhr.responseType = "json"
        xhr.onload = function () {
            if (this.status == 200) {
                resolve(this.response)
            } else {
                reject(this.statusText)
            }
        }
        xhr.send();
    })
}

//Generator函數
function* main() {

    //請求classes.json資料,并傳回包含Promise的對象,且暫停函數
    let className = yield ajax("/api/classes.json")
    console.log("className:", className) //className: A1班
    
    //請求users.json資料,并傳回包含Promise的對象,且暫停函數
    let userName = yield ajax("/api/users.json")
    console.log("userName:", userName)
}

//調用Generator函數,并啟用Generator函數
let generator = main()
let classGenerator = generator.next()
console.log("classGenerator:", classGenerator) //classGenerator: {value: Promise, done: false}

//檢查Generator函數是否完成
if (classGenerator.done) return
let classPromise = classGenerator.value//擷取Promise對象

//擷取ajax請求到資料
classPromise.then(res => {
    let className = res[0].className
    
    //拿到資料後,再次啟動Generator函數,并将所需參數傳遞到Generator函數内容
    //擷取到下一個yield所在的ajax調用,并傳回包含該調用的Promise的Generator對象
    let userGenerator = generator.next(className)
    console.log("userGenerator:", userGenerator) //userGenerator: {value: Promise, done: false}
    
    //檢查Generator函數是否完成
    if (classGenerator.done) return
    let userPromise = userGenerator.value
    
    userPromise.then(userRes => {
        let userName = userRes[0].name
        console.log(userName)
        //如果還有其他的調用,則繼續...
    })
})
           

Generator的執行器

  • 利用遞歸的方法,簡化上面的代碼

    (users.json)

[
    {
        "name": "asd",
        "age": 32
    },
    {
        "name": "bbb",
        "age": 12
    }
]
           

(classes.json)

[
    {
        "className": "A1班",
        "level": "三年級"
    },
    {
        "className": "A5班",
        "level": "二年級"
    }
]
           

(js)

console.log(`------------------------------\n----Generator的執行器\n------------------------------`)

//請求函數
const ajax = function (url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open("GET", url)
        xhr.responseType = "json"
        xhr.onload = function () {
            if (this.status == 200) {
                resolve(this.response)
            } else {
                reject(this.statusText)
            }
        }
        xhr.send();
    })
}

//需調用接口的函數
function* generatorFn() {
   try {
        console.log("generator")
        
        let users = yield ajax("/api/users.json")
        console.log("generator users:", users) //generator users: (2) [{…}, {…}]
        
        let classes = yield ajax("/api/classes.json")
        console.log("generator classes:", classes) //generator classes: (2) [{…}, {…}]
        
        //異常處
        let errorTest = yield ajax("/api/error-path.json")
        console.log("generator errorTest:", errorTest)
        
    } catch (error) {
        console.log(error)
    }
}

//Generator的執行器
function co(generatorFn) {

    const generator = generatorFn()
    
    //遞歸方法
    function handleResult(result) {
    
        if (result.done) return//Generator函數執行結束,則停止遞歸
        
        result.value.then(res => {
            //啟動Generator函數,并遞歸
            handleResult(generator.next(res))
        })
            .catch(err => {//使用catch捕獲異常
                generator.throw(err)
            })
    }
    
    //使用Generator函數,隻需啟用一次
    handleResult(generator.next())
}

//使用執行器
co(generatorFn)
           

async 和 await 文法糖

  • 與 * 和 yield 的使用差不多
  • 不需要執行器,直接調用即可
  • await隻會出現在async修飾的函數内部

    (簡化上述代碼)

console.log(`------------------------------\n----async和await文法糖\n------------------------------`)

//請求函數
const ajax = function (url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest()
        xhr.open("GET", url)
        xhr.responseType = "json"
        xhr.onload = function () {
            if (this.status == 200) {
                resolve(this.response)
            } else {
                reject(this.statusText)
            }
        }
        xhr.send();
    })
}

//需調用接口的函數
//将 * 換成 async
//将 yield 換成 await
async function generatorFn() {
    console.log("generator")
    
    let users = await ajax("/api/users.json")
    console.log("generator users:", users) //generator users: (2) [{…}, {…}]
    
    let classes = await ajax("/api/classes.json")
    console.log("generator classes:", classes) //generator classes: (2) [{…}, {…}]
    
    //異常處
    let errorTest = await ajax("/api/error-path.json")
    console.log("generator errorTest:", errorTest)
}

//直接調用Generator函數即可
//不需要執行器,因為它是語言層面的,标準異步程式設計文法
//傳回一個Promise的對象
generatorFn().then(res => {
    console.log("all complete!")
}).catch(err => {
    console.log(err)
})
           

繼續閱讀