002 Generator 函数

7/18/2021 Javascript

协程: 意思是多个线程互相协作,完成异步任务。

# 用法

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。语法上,Generator 函数是一个状态机,封装了多个内部状态。该在function关键字与函数名之间有一个星号。

Generator 函数最大的特点就是可以通过 yield关键字 暂停函数执行,交出控制权。函数体外则通过 next() 方法再次交回控制权

function* foo () {
	const a = yield 2
	return 88
}
const g = foo(); // 此时未执行,只生成对象
console.log(g.next()); // 第一次执行 // {value: 2, done: false}
console.log(g.next()); // 第二次执行 // {value: 88, done: true}
1
2
3
4
5
6
7

next 方法可以接收参数,这是向 Generator 函数体内输入数据,但第一次使用next方法传参是无效的,方法的参数表示上一个yield表达式的返回值

 function* foo() {
    const a = yield 2;
    console.log(a); // 123
    return 88;
  }
  const g = foo();
  console.log(g.next()); // 第一次执行返回第一个 yield 表达式的值
  console.log(g.next(123)); // 第二次执行 并传入123,改变了第一次执行的 a 的值
1
2
3
4
5
6
7
8

除了 next 方法还有 throw() 、 return()

  • throw()是将yield表达式替换成一个throw语句

    gen.throw(new Error('error')); // Uncaught Error: 出错了
    
    1
  • return()是将yield表达式替换成一个return语句从而终止函数

    gen.return(2); // {value: 2, done: true}
    
    1

# 应用

# 单个异步任务

function* foo() {
  let res = yield fetch("https://jsonplaceholder.typicode.com/todos/1");
  console.log(res);
}
1
2
3
4

获取结果

const g = foo()
const result = g.next() // 第一次执行遇到yield, 返回 fetch 结果 {value: Promise, done: false}
result.value.then(data => {
    return data.json(); // json格式化
}).then(res => {
    // 拿到格式化后的数据,交回给 foo ,同时第二次执行 next 改变的是上一次的值,res 正常输出
    g.next(res)
})
1
2
3
4
5
6
7
8

# 多个异步任务

function* foo() {
    let res = yield fetch("https://jsonplaceholder.typicode.com/todos/1");
    console.log("res1", res);
    let res2 = yield fetch("https://jsonplaceholder.typicode.com/todos/2");
    console.log("res2", res2);
}
1
2
3
4
5
6

为了正常运行可能需要链式调用then

const g = foo();
const result1 = g.next();
result1.value.then((res) => {
        return res.json();
	}).then((data) => {
        return g.next(data).value; // 返回promise对象
	}).then((res) => {
        return res.json();
	}).then((data) => {
        return g.next(data);
});
1
2
3
4
5
6
7
8
9
10
11

有更好的方法吗?可以使用递归编写一个启动器,让 Generator 函数自动运行 (dva.js -- effect)

function run(gen) {
    var g = gen();
    function next(data) {
        var result = g.next(data);
        if (result.done) return;
        result.value.then(function (data) {
            return data.json();
        }).then(function (data) {
            next(data);
        });
    }
    next();
}
run(foo); // 传入 Generator 函数 自动执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14

参考: 小小小十七 (opens new window) 阮一峰 (opens new window) 阿里云云栖号 (opens new window)

Last Updated: 8/5/2021, 6:26:40 PM