generator函数与普通函数的区别:多个*,并且只有在generator函数中才能使用yield,yield相当于generator函数执行的中途暂停点,可以通过.next继续执行下一个,.next方法返回一个对象,{value: any, done: Boolean}
- value: 暂停点后面接的值,即yield后面的值
- done:generator函数是否已经结束,是 true,否 false
js
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后面接函数
js
// 执行到了对应的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
js
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的传参
js
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传参
js
function fn(num) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * 2);
}, 1000);
});
}
function* gen() {
const test2 = yield fn(1);
console.log(test2); // 2
const test3 = yield fn(test2);
console.log(test3); // 4
const test4 = yield fn(test3);
console.log(test4); // 8
return test4;
}
const g = gen();
g.next().value.then((test2) => {
console.log(test2); // 2
g.next(test2).value.then((test3) => {
console.log(test3); // 4
g.next(test3).value.then((test4) => {
console.log(test4); // 8
g.next(test4);
});
});
});
二、async/await
generator函数的Promise & next传参,类似async/await,区别在于:
- gen函数的执行返回值不是Promise,asyncFn执行(即asyncFn())返回值是Promise;
- gen函数需要执行相应的操作,才能等同async Fn的排队效果
- gen函数执行的操作是不完善的,因为并不确定有多少yield,不确定会嵌套几次
针对这种情况,可以通过高阶函数(HOC)封装:
高阶函数: 参数是函数,返回值也可以是函数
js
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
js
// 以上完成了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,做出以下改造:
js
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