RxJS中的排序导致“内存不足”错误

时间:2017-03-14 22:08:27

标签: javascript node.js promise rxjs reactive-programming

我需要处理100万行记录,转换它们中的每一行,然后将它们保存到多个文件中(按小时分箱;每小时1个文件 - 我想用“过滤器”拆分它们)

出于某种原因,我需要严格按顺序处理这些行。意思是,如果第#450000行需要更长的时间来处理和保存(这是棘手的部分,因为fs与回调异步),处理不会跳转到#450001 ...它将等到450000完成。在代码中随机休眠就是模拟那种情况。

以前(使用简单的Promise,没有RxJ),我会创建N个promise,每行一个,将它们保存在一个数组中,然后通过reduce op进行链接,如下所述:https://github.com/kriskowal/q

但我不想创建100万个Promise实例。所以,我调查了ReactiveX,希望它会像“推卸责任”;意味着它不会等待,一旦事件弹出就会发生处理,并且处理所使用的资源(认为处理块基本上是场景背后的承诺)将尽快释放。

我尝试使用此代码验证:

import Rx from 'rxjs-es6/Rx';
import Q from 'q';    

let subject = new Rx.Subject();
let processEventJsons = function(observable) {
  observable.concatMap(eventJson => {
    let deferred = Q.defer();    

    setTimeout(() => {
      eventJson.procDatetime = new Date().toISOString();
      deferred.resolve(eventJson);
    }, Math.random() * 5000);    

    return Rx.Observable.fromPromise(deferred.promise)
  })
  .subscribe({
    next: enrichedEventJson => {
      console.log(JSON.stringify(enrichedEventJson));
    },
    error: err => console.error('something wrong occurred: ' + err),
    complete: () => console.log('done'),
  });
}    

processEventJsons(
  subject.filter(dataJson => dataJson.type === "interview").map(dataJson => {
    return {event: "intv", datetime: dataJson.datetime}
  })
)    

processEventJsons(
  subject.filter(dataJson => dataJson.type === "checkin").map(dataJson => {
    return {event: "chki", datetime: dataJson.datetime}
  })
)    

for (let i = 0; i < 1000000; i++) {
  if (Math.random() < 0.5) {
    subject.next({id: i, type: "interview", datetime: new Date().toISOString()});
  } else {
    subject.next({id: i, type: "checkin", datetime: new Date().toISOString()});
  }
}
subject.complete();

但是......我一直在接受:

  

致命错误:CALL_AND_RETRY_LAST分配失败 - JavaScript堆内存不足。

console.log(JSON.stringify(enrichedEventJson));直到“for-loop”(代码末尾)完成后才会打印。

这让我觉得转换到RxJS并没有真正改善这种情况;它仍然在现场排队等待承诺。

或者我错误地使用了API?你能帮我指出什么是错的吗?

更新更新:

假旗。弄清楚问题不在于使用RxJS,而是在for-loop中(它太紧了)。所以我改为:

for (let i = 0; i < 1000000; i++) {
  if (Math.random() < 0.5) {
    setTimeout(() => {
      subject.next({id: i, type: "interview", datetime: new Date().toISOString()});
    });
  } else {
    setTimeout(() => {
      subject.next({id: i, type: "checkin", datetime: new Date().toISOString()});
    });
  }
}

1 个答案:

答案 0 :(得分:2)

  

我会创建N个promise,每行一个,将它们保存在一个数组中,然后通过reduce op进行链接

这是一种简单但需要内存的方法。它使用了需要同时存在的一百万个承诺。相反,您可以使用递归方法在常量内存中按顺序处理行:

function getInput(i) {
  return {id: i, type: Math.random() < 0.5 ? "interview" : "checkin", datetime: new Date().toISOString()};
}
function process(eventJson) {
  let deferred = Q.defer();    
  setTimeout(() => {
    eventJson.procDatetime = new Date().toISOString();
    deferred.resolve(eventJson);
  }, Math.random() * 5000);    
  return deferred.promise;
}
function filteredProcess({type, datetime}) {
  if (type === "interview")
    return process({event: "intv", datetime});
  if (type === "checkin")
    return process({event: "chki", datetime});
}
function log(enrichedEventJson) {
  console.log(JSON.stringify(enrichedEventJson));
}

function loop(i) {
  if (i < 1000000)
    return getInput(i)
    .then(filteredProcess)
    .then(log)
    .then(() => loop(i+1));
  else
    return Q("done")
}

loop().then(console.log, err => console.error('something wrong occurred: ' + err));