Rx.js等待回调完成

时间:2015-12-04 16:49:57

标签: javascript reactive-programming rxjs

我正在使用Rx.js处理文件的内容,为每一行发出http请求,然后聚合结果。但是源文件包含数千行,我正在重载我正在执行http请求的远程http api。我需要确保在开始另一个http请求之前等待现有的http请求回调。我一次打开批处理和执行n个请求,但是对于这个脚本执行请求就足够了。

我有以下内容:

const fs = require('fs');
const rx = require('rx');
const rxNode = require('rx-node');

const doHttpRequest = rx.Observable.fromCallback((params, callback) => {
  process.nextTick(() => {
    callback('http response');
  });
});

rxNode.fromReadableStream(fs.createReadStream('./source-file.txt'))
  .flatMap(t => t.toString().split('\r\n'))
  .take(5)
  .concatMap(t => {
    console.log('Submitting request');

    return doHttpRequest(t);
  })
  .subscribe(results => {
    console.log(results);
  }, err => {
    console.error('Error', err);
  }, () => {
    console.log('Completed');
  });

但是,这不会串行执行http请求。它输出:

Submitting request
Submitting request
Submitting request
Submitting request
Submitting request
http response
http response
http response
http response
http response
Completed

如果我删除对concatAll()的调用,则请求是串行的,但我的订阅函数在http请求返回之前看到了observable。

如何以串行方式执行HTTP请求,以便输出如下?

Submitting request
http response
Submitting request
http response
Submitting request
http response
Submitting request
http response
Submitting request
http response
Completed

1 个答案:

答案 0 :(得分:1)

这里的问题可能是当你使用rx.Observable.fromCallback时,你在参数中传递的函数会立即执行。返回的observable将保留稍后传递给回调的值。为了更好地了解正在发生的事情,您应该使用稍微复杂的模拟:对您的请求进行编号,让它们返回您可以通过订阅观察到的实际(每个请求不同)结果。

我在这里发生了什么:

  • take(5)发出5个值
  • map发出5条日志消息,执行5个函数并传递5个observables
  • 这5个可观察对象由concatAll处理,这些可观察对象发布的值将按预期顺序排列。你在这里订购的是调用函数的结果,而不是对函数本身的调用。

为了实现您的目标,您只需在rx.Observable.fromCallback订阅它而不是在创建时调用您的可观察工厂(concatAll)。为此,您可以使用deferhttps://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/defer.md

所以你的代码会变成:

rxNode.fromReadableStream(fs.createReadStream('./path-to-file'))
  .map(t => t.toString().split('\r\n'))
  .flatMap(t => t)
  .take(5)
  .map(t => {
    console.log('Submitting request');

    return Observable.defer(function(){return doHttpRequest(t);})
  })
  .concatAll()
  .subscribe(results => {
    console.log(results);
  }, err => {
    console.error('Error', err);
  }, () => {
    console.log('Completed');
  });

您可以在此处看到类似的问题,并提供了一个很好的解释:How to start second observable *only* after first is *completely* done in rxjs

您的日志可能仍会连续显示5条“提交请求”消息。但是你的请求应该按照你的意愿一个接一个地执行。