第 18 章 Generator 函数的异步应用

Generator 函数的异步应用

1. 传统方法

  • 回调函数

    1
    2
    3
    4
    fs.readFile('./etc/passwd', 'utf-8', function (err, data) {
    if (err) throw err;
    console.log(data);
    });
  • 事件监听

  • 发布/订阅
  • Promise 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    let readFile = require('fs-readfile-promise');

    readFile(fileA)
    .then(function (data) {
    console.log(data.toString());
    })
    .then(function () {
    return readFile(fileB);
    })
    .then(function (data) {
    console.log(data.toString());
    })
    .catch(function (err) {
    console.log(err);
    });

2. Generator 函数

  • 协程流程:
    1. 协程 A 开始执行
    2. 协程 A 执行到一半,进入暂停,执行权转移到协程 B
    3. (一段时间后)协程 B 交还执行权
    4. 协程 A 恢复执行
1
2
3
4
5
6
// 读取文件的协程写法
function* asyncJob() {
// ...其他代码
let f = yield readFile(fileA);
// ...其他代码
}
  • 协程的 Generator 函数实现

    1
    2
    3
    4
    5
    6
    7
    8
    function* gen(x) {
    let y = yield x + 2;
    return y;
    }

    let g = gen(1);
    gen.next(); // {value: 3, done: false}
    gen.next(); // {value: undefined, done: true}
  • Generator 函数的数据交换和错误处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    function* gen(x) {
    let y = yield x + 2;
    return y;
    }

    let g = gen(1);
    gen.next(); // {value: 3, done: false}
    gen.next(2); // {value: 2, done: true}

    // Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误
    function* gen(x) {
    try {
    let y = yield x + 2;
    } catch (e) {
    console.log(e);
    }
    return y;
    }

    let g = gen(1);
    g.next();
    g.throw('出错了'); // 出错了
  • 异步任务的封装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    let fetch = require('node-fetch');

    function* gen() {
    let url = 'https://api.github.com/users/github';
    let result = yield fetch(url);
    console.log(result.bio);
    }

    let g = gen();
    let resule = g.next();

    result.value.then(function(data){
    return data.json();
    }).then(function(data){
    g.next(data);
    });

3. Thunk 函数

  • Generator 函数的流程管理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    // Generator 函数可以自动执行, 不适合异步操作
    function* gen() {
    // ...
    }

    let g = gen();
    let res = g.next();

    while(!res.done) {
    console.log(res.value);
    res = g.next();
    }

    // Generator 函数封装了两个异步操作
    let fs = require('fs');
    let thunkify = require('thunkify');
    let readFileThunk = thunkify(fs.readFile);

    let gen = function* () {
    let r1 = yield readFileThunk('./etc/fstab');
    console.log(r1.toString());
    let r2 = yield readFileThunk('./etc/shells');
    console.log(r2.toString());
    };

    // 手动执行 Generator 函数
    let g = gen();

    let r1 = g.next();
    r1.value(function(err, data) {
    if (err) throw err;
    let r2 = g.next(data);
    r2.value(function(err, dara){
    if (err) throw err;
    g.next(data);
    });
    });

    // Thunk 函数的自动流程管理
    function run(fn) {
    let gen = fn();

    function next(err, data) {
    let result = gen.next(data);
    if (result.done) return;
    result.value(next);
    }
    next();
    }

    function* g() {
    // ...
    }

    run(g);

5. co 模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 一个 Generator 函数,用于依次读取两个文件
let gen = function* () {
let f1 = yield readFile('./etc/fstab');
let f2 = yield readFile('./etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};

// co 模块可以不用编写 Generator 函数的执行器
let co = require('co');
co(gen);

// co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数
co(gen).then(function () {
console.log('Generator 函数执行完成');
})
  • 基于 Promise 对象的自动执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 把 fs 模块的 readFile 方法包装成一个 Promise 对象
    let fs = require('fs');

    let readFile = function(fileName) {
    return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data){
    if (error) return reject(error);
    resolve(data);
    });
    });
    };

    // 手动执行 Generator 函数
    let gen = function* () {
    let f1 = yield readFile('./etc/fstab');
    let f2 = yield readFile('./etc/shells');
    console.log(f1.toString());
    console.log(f2.toString());
    }

    // 自动执行器
    function run(gen) {
    let g = gen();

    function next(data) {
    let result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data) {
    next(data);
    });
    }
    next();
    }

    run(gen);
-------------本文结束 感谢您的阅读-------------
您的支持将鼓励我继续创作!