Generator+CO 模拟Async/Await

347 阅读2分钟

Generator函数的语法

我们前端在日常开发中,经常会遇到我们当前请求的参数,需要从上一个请求的返回结果中获取,比如:

ajax('url1',params1, res1=>{
    ajax('url2', res1.params2, res2=>{
        console.log(res2);
        // ...
    })
})

如果多个请求有依赖关系,那么将会造成前端常见的回调地狱。这个时候Promise出现了,解决了层层嵌套的问题

ajax('url1', params1)
    .then(res1=> {
        return ajax('url2', res1.params);
    })
    .then(res2=> {
        console.log(res2);
    })

但是这种写法,还是会有.then().then()的拼接,虽然解决了层层嵌套,但是写起来依然很不爽。后来Async/Await出现了

async function load() {
    const res1 = await ajax('url1', params1);
    const res2 = await ajax('url2', res1.params2);
    
    console.log('清爽无比')
}

load();

Async/Await是Generate+Co库的JS原生支持版本,那么它之前是长什么样子的呢?

在node环境下,我新建两个文件age.txt,name.txt

// name.txt
I am xiaoming
// age.txt
I am 18 years old

我要异步读取这两个文件,先读name.txt,再读age.txt

const fs = require('fs').promises; // node自带模块,读取文件返回一个Promise

function* read() {
    const name = yield fs.readFile('./name.txt', 'utf8');
    const age = yield fs.readFile('./age.txt', 'utf8');
    
    return {
        name,
        age
    }
}
const iterator = read();
const {value, done} = iterator.next(); // 第一个yield执行,返回一个Promise

// 为了让name有值,我们必须在next('name')中传参过去,否则name为undefined
// 所以我们需要再then中把resolve的值传递过去
value.then(data=> {
    const {value, done} = iterator.next(data); // 给name赋值,执行下一个yield
    
    return value;
}).then(data=> {
    const {value, done} = iterator.next(data); // 给age赋值,return;
    
    console.log(value, done); // { name: 'I am xiaoming', age: 'I am 18 years old' } true
})

为了优化.then().then()写法,我们把read()函数包装一下

const co = iterator=> {
    return new Promise((resolve, reject)=> {
        // 然后我们递归迭代这个函数,因为是generator的特性,我们第一步就要next它
        next();
        
        function next(data) {
            const {value, done} = iterator.next(data);
            
            if (!done) {
                // 如果没有结束就一直往下迭代,value可能不是一个Promise,我们包装一下
                Promise.resolve(value).then(next, reject);
            } else {
                // 最后的return值
                resolve(value);
            }
        }
    })
}
co(read()).then(data=> {
    console.log(data) // { name: 'I am xiaoming', age: 'I am 18 years old' }
})