我试图读取和解析一个大型的csv文件,并且对于每一行我都必须进行一些异步计算,并在操作完成后增加计数器。 因此,我创建了一个Promise p并尝试链接许多.then(xxx)然后在csv的末尾读取它是输出计数的最终.then(yyy)

但是,此数字不累加。 但是,如果我执行p = p.then(xxx)p = p.then(yyy)则数字将加起来(对于较小的csv文件),但有时(对于较大的csv文件)我将面临内存泄漏。

我做错了什么吗?

var fs = require('fs')
const csv = require('fast-csv');
var Promise = require('bluebird')
var count = 0;
var actual = 0;
let p = Promise.resolve();
const stream = fs.createReadStream(`/Users/ssmlee/Desktop/KingKong_Sims_5M.txt`);
const csvStream = csv({
    delimiter: ';'
})
.on('data', (row) => {
    count++
    if (count % 10000 === 0) {
        console.log(count)
        console.log(process.memoryUsage().heapUsed)
    }
  p.then(() => { // instead if we do p = p.then(() => it will work correctly
    return Promise.resolve().delay(5)
    .then(function() {
        actual++
    })
  });
})
.on('end', () => {
  p.then(() => { // instead if we do p = p.then(() => it will work correctly
    console.log(actual); // 4999977 or something like this
    console.log(count); // 5000000
  });
});
stream.pipe(csvStream);

#1楼 票数:1 已采纳

如果您延迟增加actual计数,但从不等待诺言(将then的结果扔掉),则流可能会以并非所有增量都已发生而结束。 在您的示例中,有23个回调仍在等待5毫秒的延迟。 顺便说一句,将所有这些链接在同一p = Promise.resolve()上没有多大意义,您可以立即执行所有操作。

如果您正在执行p = p.then(…)则会建立一个很长的承诺链。 这不会泄漏任何内存,但会占用大量内存-所有5毫秒的延迟都按顺序链接在一起,并且脚本将(至少)需要25000秒才能运行。 一开始就读入文件,生成了数百万个promise,然后它们一个接一个地解析(可以被垃圾回收)。

为了高效地执行此顺序方法,您可能应该使用物流的背压系统。

但是,您也可以同时等待延迟,一次没有太多活动承诺:

p = Promise.all([p, Promise.delay(5)]).then(() => {
  actual++;
});

#2楼 票数:1

好吧,您希望这些承诺可以并行运行,所以不能将它们链接在一起。

allp = [];
....
.on('data', (row) => {
    ...
    allp.push( p.then(() => {...}) );
}
...
.on('end', () => {
Promise.all(allp).then(() => {})

当然,您要为每个事件创建一个Promise。

如果您需要在结束前释放承诺,那么您需要自己进行。

由于您似乎对承诺的回报价值不感兴趣,而仅对它们的副作用(计数增加)感兴趣,因此您可以

.on('data', (row) => {
    ...
    if (allp.length > 50) allp = [Promise.all(allp).then(()=>null)];
    allp.push( p.then(() => {...}) );
}

这样,将对50个诺言进行分组,一旦解决,便将它们替换为一个诺言(它将进入下一个50 ...)

.then(()=>null)确保Promise.all的结果数组也被丢弃。 (取而代之的是一个对null的承诺)

这确实取决于Promise.all的实现。 如果Promise.all在解决时(并且结果可用)释放每个promise,那么这是完美的。

如果Promise.all等待所有50个诺言,然后将其全部释放,则这仍然有效,除非每50个组都有一个运行时间非常长的诺言。


您可以使用延迟承诺的反模式。

在开始时创建一个递延承诺。

var resolve;
var asyncRunningCount = 1; // start with 1
var p2 = new Promise(function() {
    resolve = arguments[0];
});

在上数据

.on('data', (row) => {
    ...
    asyncRunningCount++;
    p.then(() => {work}) )
    .then(() { 
        asyncRunningCount--; 
        if (asyncRunningCount == 0) resolve(); // no more tasks running
    } );
}

.on('end', () => {
    asyncRunningCount--; 
    // remove the 1 that was set on start. No more new tasks will be added
    if (asyncRunningCount == 0) resolve(); // no more tasks running
    p2.then(() => { all done })

如果正在运行的任务数暂时降至0,则启动时的值1会阻止解析p2。

在on(end)中,1递减。 如果所有任务都完成,则asyncRunningCount将为0。这可以通过on(end)的递减或on(data)的递减来实现。

p2.then将在所有任务完成后运行。

其他所有诺言完成后将被释放。 实际上,在on(data)中,您不需要承诺。 只需启动您的异步任务,然后在完成异步任务后递减asyncRunningCount并检查0。


这仍然意味着,如果数据输入速度非常快,那么许多承诺就会并行开始。 但是,如果您不履行承诺,那么您需要存储传入的数据,因此将以任何一种方式使用内存。

  ask by Shih-Min Lee translate from so

未解决问题?本站智能推荐:

1回复

JavaScript许诺.then()无法执行

我正在尝试下载一堆文件,为每个文件存储一些数据,然后使用该数据进一步处理它们。 稍后,我将使用Promise.map(queue,(x)=>get_image(...x),{concurrency: 1}).all().then( )之后将它们一起处理。 但是,在我的.then()调用
1回复

Promise.all中的罐头(ES6和Bluebird)

此安全装置是否必要? 是否有可能和安全地将罐子提供给收集方法Promise.all , Promise.race等? 有什么陷阱吗? 问题与Bluebird以及polyfilled和所有本机ES6 Promise实现有关。
2回复

许诺中的承诺没有按顺序使用每个

我在节点脚本中使用了Bluebird Promise库,但在理解代码执行顺序时遇到了麻烦。 我有一个使用.each方法的Promises数组,因此它们按顺序依次执行。 在每个这些诺言中,我都有两个链接的诺言,但是第二个诺言并未按我期望的那样执行。 如果我们采用以下代码,我会认为输出为0.
3回复

节点bluebird许诺链[重复]

这个问题已经在这里有了答案: 如何使用Promises链接和共享先前的结果[重复] 1个答案 我是新来的保证。 我正在尝试使用Promise将查询发送到mysql db。 在进行一些查询之后,我将使用查询的结果,进行一些计算,然后将输出用作下一个查询的某些参数。 看起来如
2回复

循环链接JavaScript许诺-蓝鸟

我一直在阅读有关使用bluebird的诺言,并试图实现以下目标: 我需要运行func1和func2,直到循环结束。 完成后,我想运行func3。 代码如下: 注意:所有函数都有一个回调函数(以表明操作已成功完成) func1和func2是使用promises完成的,从而在回调函数
1回复

在Promose/bluebird中顺序执行命令

我有一个需要追加到数据库的数组,条件是,元素必须一个接一个地追加才能使其工作,以下是我的代码,似乎命令没有顺序执行,我的代码有什么问题:谢谢。
2回复

执行中间动作,并在bluebird中延续先前的结果

是否有一个速记可以允许获得Promise的价值并将其推向承诺链,而无需显式地将其返回? 换句话说,bluebird中是否存在以下内容的简写语法:
2回复

仅在第一个未返回良好结果的情况下执行其他承诺

因此,我为此苦苦挣扎了几天,并且找到了解决方案,但是我觉得这不是一个好方法。 我目前有以下。 我不喜欢它,因为我在Promise中嵌套了Promises。 我不知道这是否还好,但看起来并不像。 我要用此代码完成的工作是,首先检查高速缓存数据库中是否存在一个值,然后再检查真实数据库。