generator函数和async/await
generator函数与普通函数的区别:多个*,并且只有在generator函数中才能使用yield,yield相当于generator函数执行的中途暂停点,可以通过.next继续执行下一个,.next方法返回一个对象,{value: any, done: Boolean}
- value: 暂停点后面接的值,即yield后面的值
- done:generator函数是否已经结束,是 true,否 false
function* gen() {yield 1;yield 2;yield 3;// return 8;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true } | { value: 8, done: true } 最后一个value值取决于generator函数有没有返回值
一 、generator
1. yield后面接函数
// 执行到了对应的yield时,会立刻执行此函数,并且函数的返回值会被赋给yield暂停点对象value
function fn(num) {return num * 2;
}function* gen() {yield fn(1);yield fn(2);return 10;
}
const g = gen();
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 4, done: false }
console.log(g.next()); // { value: 10, done: true }
2. yield后面接Promise
function fn(num) {return new Promise((resolve) => {setTimeout(() => {resolve(num * 2);}, 1000);});
}function* gen() {yield fn(1);yield fn(2);return 10;
}const g = gen();
// console.log(g.next()); // { value: Promise { <pending> }, done: false }
// console.log(g.next()); // { value: Promise { <pending> }, done: false }
// console.log(g.next()); // { value: 10, done: true }// 若想要获取promise的结果,可以使用.then
g.next().value.then((val1) => {console.log(val1);g.next().value.then((val2) => {console.log(val2);});
});
3. next函数传参
generator函数可以用next方法来传参,并且可以通过yield来接收这个入参,注意以下两点:
- 第一次next传参是没有作用的,只有从第二次开始next传参才有用
- next传值时,顺序为:先执行右边的yield,然后左边接收的是下一次next的传参
function* gen() {const test2 = yield 1;console.log(test2);const test3 = yield 2;console.log(test3);return 10;
}const g = gen();
console.log(g.next());
// { value: 1, done: false }console.log(g.next("test2"));
// test2
// { value: 2, done: false }console.log(g.next("test3"));
// test3
// { value: 10, done: true }
4. Promise & next传参
function fn(num) {return new Promise((resolve) => {setTimeout(() => {resolve(num * 2);}, 1000);});
}function* gen() {const test2 = yield fn(1);console.log(test2); // 2const test3 = yield fn(test2);console.log(test3); // 4const test4 = yield fn(test3);console.log(test4); // 8return test4;
}const g = gen();
g.next().value.then((test2) => {console.log(test2); // 2g.next(test2).value.then((test3) => {console.log(test3); // 4g.next(test3).value.then((test4) => {console.log(test4); // 8g.next(test4);});});
});
二、async/await
generator函数的Promise & next传参,类似async/await,区别在于:
- gen函数的执行返回值不是Promise,asyncFn执行(即asyncFn())返回值是Promise;
- gen函数需要执行相应的操作,才能等同async Fn的排队效果
- gen函数执行的操作是不完善的,因为并不确定有多少yield,不确定会嵌套几次
针对这种情况,可以通过高阶函数(HOC)封装:
高阶函数: 参数是函数,返回值也可以是函数
function fn(nums) {return new Promise((resolve) => {setTimeout(() => {resolve(nums * 2);}, 1000);});
}function* gen() {const num1 = yield fn(1);const num2 = yield fn(num1);const num3 = yield fn(num2);return num3;
}function generatorToAsync(genFn) {return function () {return new Promise((resolve, reject) => {const g = genFn();g.next().value.then((num1) => {g.next(num1).value.then((num2) => {g.next(num2).value.then((num3) => {resolve(g.next(num3).value);});});});});};
}const asyncFn = generatorToAsync(gen); // 返回Promise
asyncFn().then((val) => console.log(val)); // 3s后 打印8
// 以上完成了async/await基础功能,等同于:
async function asyncFn() {const num1 = await fn(1);const num2 = await fn(num1);const num3 = await fn(num2);return num3;
}
asyncFn().then((res) => console.log(res));
但是上述的async await数量是固定的,对于不固定数量的await,做出以下改造:
function fn(nums) {return new Promise((resolve, reject) => {setTimeout(() => {// resolve(nums * 2);// reject("err");}, 1000);throw "err";});
}function* gen() {const num1 = yield fn(1);const num2 = yield fn(num1);const num3 = yield fn(num2);const num4 = yield fn(num3);const num5 = yield fn(num4);return num5;
}function generatorToAsync(genFn) {return function () {return new Promise((resolve, reject) => {const g = genFn.apply(this, arguments); // genFn可能存在入参function goNext(key, nextArg) {let yieldValue;try {yieldValue = g[key](nextArg);const { value, done } = yieldValue;// value有可能是:常量,Promise,Promise有可能是成功或者失败if (value instanceof Promise) {if (done) {// 如果done为true,说明走完了,进行resolve(value)resolve(value);} else {// 如果done为false,说明没走完,还得继续走value.then((res) => goNext("next", res),(err) => goNext("throw", err) // yield.throw()方法允许在Generator函数内部抛出错误,并且这个错误可以被外部的catch语句捕获);}} else {resolve(value);}} catch (error) {// 此处捕获yield.throw()抛出的异常reject(error);}}goNext("next");});};
}const asyncFn = generatorToAsync(gen); // 返回Promise
asyncFn().then((val) => console.log(val),(err) => console.log(err)
); // 3s后 打印8
