V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ufo22940268
V2EX  ›  Node.js

为什么用 co + map 的时候不会释放内存

  •  
  •   ufo22940268 · 2017-02-21 16:39:11 +08:00 · 2043 次点击
    这是一个创建于 2826 天前的主题,其中的信息可能已经有所发展或是发生改变。

    snippet1

    co(function* () {
      yield largeArray.map(function* (e) {
         const k = yield db.col.findById(e).exec()
         do something with k
      })
    })
    

    snippet2

    Promise.map(largeArray, (e) => {
       return db.col.findById(e).exec()
       	.then(k => {
        	do something with k
        })
    })
    

    这上面用的库有

    • tj/co
    • mongoose

    完成的操作就是遍历 id array ,然后把数据从 mongodb 从取出来操作下。但是经过测试,发现 snippet1 会导致内存溢出,但是 snippet2 不会。感觉好像 snippet1 在function* (){}生命周期结束之后,不会马上释放内存。没有研读过tj/co的源码,求大神指点

    第 1 条附言  ·  2017-02-22 09:10:26 +08:00

    我重新看了一下代码,发现snippet2是这样的

    Promise.map(largeArray, (e) => {
       return db.col.findById(e).exec()
       	.then(k => {
        	do something with k
        }, {concurrency: 10})
    })
    

    所以问题的根源还是largeArray太大了,同时去数据库发起太多查询,导致内存溢出。然后用了promise之后因为限制了查询一次就发起10个,等这10查询完了再进行下一个批次查询。所以没有内存溢出。


    但是还是有一个问题我没搞明白的是:

    我的mongoose设置了poolSize是10,所以同一时间建立的数据库连接就是10个,那么也就是同一时间只能进行10次查询,那为什么snippet1会内存溢出呢?

    9 条回复    2017-02-21 19:09:20 +08:00
    zbinlin
        1
    zbinlin  
       2017-02-21 17:33:02 +08:00
    把 snippet1 里的 generator 也换成 promise 试试:

    co(function* () {
      yield largeArray.map(function (e) {
         return db.col.findById(e).exec()
            .then(k => {
                do something with k
            })
      })
    })


    PS: 话说你的 largeArray 究竟有多大呀?
    binux
        2
    binux  
       2017-02-21 17:43:03 +08:00
    你的 snippet1 真的 work 吗?你只给 yield largeArray.map 包了 co , largeArray.map 也支持 generator ?
    zbinlin
        3
    zbinlin  
       2017-02-21 17:46:05 +08:00
    @binux 应该是 co 支持 generator array
    ufo22940268
        4
    ufo22940268  
    OP
       2017-02-21 17:50:46 +08:00
    @zbinlin largeArray 也不会很大,就几万,但是中间查询的结果数据量比较大
    cheetah
        5
    cheetah  
       2017-02-21 18:18:25 +08:00
    原因是,在 snippet1 中, largeArray.map 并不会等所有 callback 执行完后才返回,也就是说 Array.prototype.map 并没有 『如果 callback 返回的是个 Promose 则等待』的功能,这种情况就应该用 Promise.map
    binux
        6
    binux  
       2017-02-21 18:56:28 +08:00
    @zbinlin 确实,不过 https://www.npmjs.com/package/co#yieldables
    array (parallel execution) 这应该是问题所在
    zbinlin
        7
    zbinlin  
       2017-02-21 19:01:59 +08:00
    @binux 如果 snippet2 Promise 用的是 bluebird ,面里的 Promise.map 也是并行执行的: http://bluebirdjs.com/docs/api/promise.map.html
    binux
        8
    binux  
       2017-02-21 19:06:03 +08:00
    @zbinlin #7 那就是 snippet1 的 `do something with k` return k 了, snippet2 没有
    cheetah
        9
    cheetah  
       2017-02-21 19:09:20 +08:00
    对 co 不太熟,我把它转成 async/await 的形式是这样没错吧?

    await largeArray.map(async (e) => {
    const k = await db.col.findById(e).exec()
    do something with k
    })
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2754 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 08:45 · PVG 16:45 · LAX 00:45 · JFK 03:45
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.