异步编程解决方案generator

141 阅读2分钟

「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。

一、generator

1.1 generator函数和普通函数的对比

下面是一段普通函数代码,我们可以发现,只要满足条件,函数就会一直执行。不会中间间断。

function foo() {
    for(let i = 0;i < 3; i++) {
        console.log(i)
    }
}
foo()

下面得代码就是一段generator函数,我们可以看到function后面有*,而且会有一个yield关键字。

function* fun() {
    for(let i = 0;i < 3; i++) {
        console.log(i)
        yield i
    }
}
console.log(fun())

我们可以执行下面得代码,会发现代码并未执行。这是因为generator需要我们手动调用执行。手动调用next执行一次,这就说明generator是可以中断执行的。每调用一次,代码执行一次。返回值是一个generator对象,包含一个value和一个done,value就是yield的值,done代表这个函数是否执行完成。

function* fun() {
    for(let i = 0;i < 3; i++) {
        console.log(i)
        yield i
    }
}
let gen = fun()
console.log(gen.next()) //{value: 0, done: false}
console.log(gen.next()) //{value: 1, done: false}
console.log(gen.next()) //{value: 2, done: false}
console.log(gen.next()) //{value: undefined, done: true}

由于generator返回值是一个generator对象,所以并不能作为构造函数使用

1.2 yield关键字只能在generator函数中使用

yield关键字只能在generator函数中使用,是直接父子关系。下面得代码就会报错,因为yield出现在了一个箭头函数中,与gennerator函数不是吧直接的父子关系。

function* gen(arg) {
    arg.forEach(element => {
        yield element+1
    })
}

1.3 next()参数是上一次yield的返回值

大多数人看下面的代码执行结构肯定是一头雾水,其实我们只需要弄清楚一点,next的参数是一次yield的返回值就可以了,第一次调用传入的是5而且generator函数又是手动执行的(中断执行的),所有返回值低第一个yield后面的计算结果,也就是5+2,结果是7。后面的结果是NaN,是因为第二次第三次next()时并没有传递参数,这就导致yield(x+2)的返回值是undefined,在进行后面的计算就是NaN了。

function* gen(x) {
    let y = 2*(yield(x+2))
    let z = yield (y/2)
    return x + y + z
}

let g = gen(5)

console.log(g.next())  //{value: 7, done: false}
console.log(g.next())  //{value: NaN, done: false}
console.log(g.next())  //{value: NaN, done: true}

所以,我们每次调用时,都应该给next()传递一个参数,代码如下: 我们需要好好理解一下是如何计算的。相信搞懂generator这些特殊的地方,下面得结果肯定轻松稿定。

function* gen(x) {
    let y = 2*(yield(x+2))
    let z = yield (y/2)
    return x + y + z
}

let g = gen(5)

console.log(g.next())  //{value: 7, done: false}
console.log(g.next(12))  //{value: 12, done: false}
console.log(g.next(19))  //{value: 48, done: true}

1.4 用generator控制死循环输出

generator可以有效的控制死循环输出,让死循环不在报错。

function* fun(x=1) {
    while(true) {
        if(x%7 === 0) {
            yield x
        }
        x++
    }
}
let res = fun()
console.log(res.next().value) //7
console.log(res.next().value) //14
console.log(res.next().value) //21
console.log(res.next().value) //28

1.5 generator对异步状态进行管理

我们还是以想连续三次请求三个接口为例,使用generator对异步状态进行管理,代码如下:

function ajax(url,callback) {
    var xmlHttp 
    // 创建XMLHttpRequest
    if(window.XMLHttpRequest) {
        xmlHttp = new XMLHttpRequest()
    }else { //兼容IE7之前的浏览器
        xmlHttp = new ActiveXObject('Microsoft.XMLHTTP')
    }
    
    //发送请求 
    xmlHttp.open('GET',url,true)
    xmlHttp.send()
    
    // 响应数据
    xmlHttp.onreadystatechange = function() {
        if(xmlHttp.readyState === 4 && xmlHttp.status === 200){
            var res = JSON.parse(xmlHttp.responseText)
            callback(res)
        }
    }
}

function getData() {
    ajax(url, res => {
        g.next(res)
    })
}

function* gen() {
    let r1 = yield getData('/api1')
    let r2 = yield getData('/api2')
    let r3 = yield getData('/api3')
}

let g = gen()

g.next()