宣传流

时间:2016-12-28 09:06:37

标签: javascript node.js promise eventemitter

我试图宣传流,但它似乎比我预期的更难。这是我的尝试:

'use strict'

const Promise = require('bluebird')
const Twitter = require('twitter')

const TwitterStream = module.exports = function TwitterStream (config) {
  // init Twitter Streaming API for OAuth
  this.stream = new Twitter({
    consumer_key: config.get('/twitter/consumerKey'),
    consumer_secret: config.get('/twitter/consumerSecret'),
    access_token_key: config.get('/twitter/accessTokenKey'),
    access_token_secret: config.get('/twitter/accessTokenSecret')
  })
  .stream('statuses/filter', {
    track: config.get('/twitter/track')
  })
}

TwitterStream.prototype.receive = function () {
  return new Promise((resolve, reject) => {
    this.stream.on('data', resolve).on('error', reject)
  })
}

TwitterStream.prototype.destroy = function () {
  this.stream.destroy()
}

主要问题是当我创建对象时

const stream = new TwitterStream(config)

stream.receive().then((data) => console.log(data))

当我执行时只读取一个对象。没有其他数据流式传输。

  TwitterStream.prototype.receive = function () {
     return new Promise((resolve, reject) => {
       this.stream
         .on('data', (data) => resolve(data)
         .on('error', (error) => reject(error))
       })
    }

4 个答案:

答案 0 :(得分:1)

通过使用Rx扩展,它非常简单:

TwitterStream.prototype.receive = function () {
    return Rx.Observable.create((observer) => {
        this.stream
            .on('data', (data) => observer.onNext(data))
            .on('error', (err) => observer.onError(err));
    });
}

然后

const stream = new TwitterStream(config)

stream.receive().subscribe((data) => console.log(data));

答案 1 :(得分:0)

您需要在stream.on函数的回调中返回一个promise。现在,调用时receive方法只返回一个一旦解析后返回值或错误的promise。

答案 2 :(得分:0)

这是一个未经测试且很可能仍然有缺陷的代码,用于说明如何通过承诺来实现:

function defer() {
  var resolve, reject;
  var promise = new Promise(function() {
    resolve = arguments[0];
    reject = arguments[1];
  });
  return {
    resolve: resolve,
    reject: reject,
    promise: promise
  };
}


TwitterStream.prototype.receive = function() {

  this.stream
    .on('data', data => {
      this.dataCache = this.dataCache || [];
      this.dataCache.push(data);
      this.tryToSendData()
    })
    .on('end', () => {
      this.finished = true;
      this.tryToSendData()
    })
    .on('error', err => {
      this.lastError = err;
      // error handling still missing
    })

  return this;
}

TwitterStream.prototype.tryToSendData = function() {
  if (this.defered) {
    let defered = this.defered;
    this.defered = null;

    // if data is available or finished then pass the first element of buffer (or undefined)
    defered.resolve(this.dataCache.shift())
  }
}

TwitterStream.prototype.getNextData = function() {
  if (this.dataCache.length > 0 || this.finished) {
    // if data is available or finished then pass the first element of buffer (or undefined)
    return Promise.resolve(this.dataCache.shift());
  } else {
    // otherwise we need a defered object
    this.defered = defer();
  }
}

用法可能如下所示:

stream.receive().getNextData()
  .then(function processData(data) {
    if (data) {
      console.dir(data);

      // if data is available then continue requestin the data
      return stream.getNextData().then(processData);
    }
  })

在极少数情况下,您可以使用Deferreds。

答案 3 :(得分:0)

我想你可能想看看scramjet中我已经宣传过的流。

对于您的Twitter示例,此代码应该可以正常运行:

const stream = new Twitter({
    consumer_key: config.get('/twitter/consumerKey'),
    consumer_secret: config.get('/twitter/consumerSecret'),
    access_token_key: config.get('/twitter/accessTokenKey'),
    access_token_secret: config.get('/twitter/accessTokenSecret')
})
.stream('statuses/filter', {
   track: config.get('/twitter/track')
})
.pipe(new scramjet.DataStream)

然后执行您喜欢的任何转换...例如以某种方式映射流并在完成后将流累积到数组中。

stream.map(
    function (a) { return modifyTheTweetSomehow(a); } // a Promise can be returned here
).accumulate(
    function(a, i) { a.push(i); },
    []
) // this returns a Promise that will be resolved on stream end.

我希望你喜欢它。 :)