生成器函数基本用法,并使用生成器函数实现简单的async、await

75 阅读1分钟

一、生成器函数基本用法

Generator 函数调用和普通函数不同,它会返回一个迭代器,允许程序在函数内部定义一系列可暂停执行的点,并在需要的时候恢复执行。Generator 函数通过 function* 关键字来声明,并在其内部使用 yield 表达式来标识这些暂停点。每次调用 next() 方法时,Generator 函数会从上次离开的位置恢复执行直到遇到下一个 yield 表达式,此时它会返回一个包含 { value: ..., done: ... } 结构的对象,其中 value 是 yield 表达式后面表达式的值,done 标记着 Generator 函数是否已经完全执行完毕。

以下是一个简单的 Generator 函数示例:

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

let it = foo(5);
console.log(it.next());       // {value: 6, done: false}
console.log(it.next(12));     // {value: 8, done: false}
console.log(it.next(13));     // {value: 42, done: true}

二、实现一个简单的async、await功能

通过asyncToGenerator函数对myGenerator函数进行处理,使其可以像async、await一样来进行使用。

function asyncToGenerator (generatorFunc) {
    return function () {
        const generator = generatorFunc.apply(this, arguments);

        return new Promise((resolve, reject) => {
            function step (key, arg) {
                let generatorResult;
                try {
                    generatorResult = generator[key](arg);
                }
                catch (error) {
                    return reject(error);
                }

                const { value, done } = generatorResult;
                if (done) {
                    return resolve(value);
                }
                else {
                    return Promise.resolve(value).then(
                        val => step("next", val),
                        err => step("throw", err)
                    );
                }
            }

            return step("next");
        });
    };
}

// 写法和async和await相同
function* myGenerator () {
    try {
        // result 即为resolve抛出数据
        const result = yield new Promise(resolve => {
            // 异步事件,例:ajax,通过resolve抛出
            resolve("fqwikhfkjqw");
        });

        // console.log(result); // ===> fqwikhfkjqw
        // 返回值和async函数返回值作用相同
        return "ffffffff";
    }
    catch (error) {
        throw new Error(error);
    }
}

// 测试使用
let asyncFunc = asyncToGenerator(myGenerator);
asyncFunc();