Node.js流可以作为协程进行制作吗?

时间:2016-09-10 14:06:50

标签: node.js generator coroutine

有没有办法让Node.js作为协程流。

实施例 斐波纳契数字流。

fibonacci.on('data', cb);
//The callback (cb) is like
function cb(data)
{
    //something done with data here ...
}

期望

function* fibonacciGenerator()
{
    fibonacci.on('data', cb);
    //Don't know what has to be done further... 
};

var fibGen = fibonacciGenerator();
fibGen.next().value(cb);
fibGen.next().value(cb);
fibGen.next().value(cb);
.
.
.

从发电机中取出所需的数字。这里Fibonacci数字系列只是一个例子,实际上流可以是文件,mongodb查询结果等等。

也许是这样的

  1. 制作' stream.on'充当发电机。
  2. 将回报率放在回调函数中。
  3. 获取生成器对象。
  4. 调用next并获取流中的下一个值。
  5. 是否至少可能,如果是,如果不是为什么?也许是一个愚蠢的问题:)

2 个答案:

答案 0 :(得分:6)

如果您不想使用转换器(例如Babel)或等到async / await转到Node.js,您可以使用生成器和承诺自行实现它。

缺点是你的代码必须存在于生成器中。

首先,您可以创建一个接收流的帮助程序,并返回一个函数,该函数在调用时返回流的下一个“事件”的承诺(例如data)。

function streamToPromises(stream) {
  return function() {
    if (stream.isPaused()) {
      stream.resume();
    }

    return new Promise(function(resolve) {
      stream.once('data', function() {
        resolve.apply(stream, arguments);
        stream.pause();
      });
    });
  }
}

当你不使用它时它暂停流,并在你问下一个值时恢复它。

接下来,你有一个帮助器接收一个生成器作为参数,每次它产生一个promise,它会解析它并将其结果传递回生成器。

function run(fn) {
  var gen = fn();
  var promise = gen.next().value;

  var tick = function() {
    promise.then(function() {
      promise = gen.next.apply(gen, arguments).value;
    }).catch(function(err) {
      // TODO: Handle error.
    }).then(function() {
      tick();
    });
  }

  tick();
}

最后,您将在生成器中执行自己的逻辑,并使用run帮助程序运行它,如下所示:

run(function*() {
  var nextFib = streamToPromises(fibonacci);

  var n;

  n = yield nextFib();
  console.log(n);

  n = yield nextFib();
  console.log(n);
});
  • 您自己的生成器将产生promise,暂停执行并将控件传递给run函数。
  • run函数将解析承诺并将其值传递回您自己的生成器。

这就是它的要点。您还需要修改streamToPromises以检查其他事件(例如enderror)。

答案 1 :(得分:1)

class FibonacciGeneratorReader extends Readable {
    _isDone = false;
    _fibCount = null;
    _gen = function *() {
        let prev = 0, curr = 1, count = 1;
        while (this._fibCount === -1 || count++ < this._fibCount) {
            yield curr;
            [prev, curr] = [curr, prev + curr];
        }
        return curr;
    }.bind(this)();

    constructor(fibCount) {
        super({
            objectMode: true,
            read: size => {
                if (this._isDone) {
                    this.push(null);
                } else {
                    let fib = this._gen.next();
                    this._isDone = fib.done;
                    this.push(fib.value.toString() + '\n');
                }
            }
        });

        this._fibCount = fibCount || -1;
    }
}

new FibonacciGeneratorReader(10).pipe(process.stdout);

输出应为:

1
1
2
3
5
8
13
21
34
55