首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Node.js
Express
PPA for Ubuntu
ppa:chris-lea/node.js
Coding
V2EX  ›  Node.js

请教一个 Promise 递归的最佳实践(内存释放)

  •  
  •   lqzhgood · 63 天前 · 2824 次点击
    这是一个创建于 63 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先上代码

    let loading = false;
    (async () => {
        if (loading) return;
        loading = true;
        await getAll();
        loading = false;
    })()
    
    function getAll(page = 1) {
        return new Promise(async (resolve, reject) => {
            try {
                const body = await getPage(page);
                bigHandle(body); //body 很大 处理完需要及时释放掉
                //  body = null; <--- 尝试过这个 没有用
                if (page < 10) {
                    await getAll(++page)
                }
                resolve();
            } catch (error) {
                reject(error);
            }
        })
    }
    
    

    这段代码由于 Promise 嵌套,上一个在等下一个 Promise 完成,上一个无法被释放,最初的 Promise 需要等到 page=10 的时候洋葱模型式的层层返回后释放,pm2 中看到内存一直在飙升。。

    如果去掉 Promise,改成 异步回调的形式 一切正常,但是 loading 状态的改变就要写到回调里面去,不是很直观 这里是简化的代码,真实业务中还有一大堆状态 不想都丢到函数的回调去处理 太不优雅了。 请问在使用 Promise 的时候 这种情况的最佳实现是什么?

    // node 节点 夜间模式阅读会更舒服 日间模式 太黑了。。

    第 1 条附言  ·  63 天前
    page < 10 只是为了精简代码写的。 实际上这是一个状态值
    bigHandle(body) 就是在处理并提取这个状态值 来决定是不是要下一次 getAll。

    getAll 本来只是一个 getPage handle,只需要处理一个页面,后来发现还要处理后续页面,为了复用 bigHandle 逻辑自然就写成递归了~

    最初的需求已经变了 应该从头审视代码逻辑 而不该顺着坑往下填的。
    这里 GetAll 改成 while 死循环 里面 await 判断后 break,就行了。

    谢谢各位~ 3Q
    20 回复  |  直到 2019-10-12 14:58:10 +08:00
        1
    lllllliu   63 天前
    为什么不直接 Promise.all 展开呢。并没有看到或者说明下一次 Promise 需要前面做什么。如果需要前者可以链式调用呀,可以及时释放。
        2
    lqzhgood   63 天前
    @lllllliu 因为第二页需要第一页的数据,所以需要递归调用。 而且需要控制频率,所以不能 Promise.all 并发一把梭
        3
    ahsjs   63 天前
    把 body 变量放外面?
        4
    mcfog   63 天前 via Android
    promise 没学明白就拿 async await 来写代码就这样了

    用 new promise 来包别的 promise 已经是反模式了,还再加上 async await

    这代码没治了,从头重写吧
        5
    ayase252   63 天前
    Promise 和 async 混用感觉很奇怪....确实按#1 来说,body 在处理 getAll(page)时是不必要的。
    ```
    getPage(page)
    .then((body) => {
    bigHandle(body)
    })
    .then(() => {
    getAll(++page)
    }, (error) => {
    //...
    })
    .
    ```
        6
    yixiang   63 天前   ♥ 1
    写 node 从不关心内存占用……感觉是伪命题。

    但你这个可能可以这么解决。

    ```
    var body;
    for (var i = 1; i < 10; i ++) {
    body = await getPage(page);
    bigHandle(body);
    }
    ```

    为啥想不开要递归……
        7
    lqzhgood   63 天前
    @ahsjs 感觉不是单纯的 body 问题。body 再怎么大也就 1m 的纯文本。内存几十兆几十兆的涨。
    主要是 Promise 整个堆栈没有释放,这个才是内存爆炸的主要原因。
    但是 递归的 Promise 怎么合理的释放上一个 Promise 感觉这是个悖论了……
    所以来问问有没有这类问题的最佳实践。

    难道只能回到 回调地狱 来处理了么~
        8
    jifengg   63 天前
    同意 @mcfog 说的。
    getAll 完全可以不用 Promise,也不要用递归。里面写个 for 循环就好了。有了 await /async,完全可以把 node 异步当成同步来开发。
        9
    ahsjs   63 天前
    let loading = false;
    (async () => {
    if (loading) return;
    loading = true;
    for (let i = 0; i < 10; i++) {
    let body = await getPage(page);
    bigHandle(body); //body 很大 处理完需要及时释放掉
    }
    loading = false;
    })()
        10
    lllllliu   63 天前
    emmm,Page 数量是提前可以知道的么? 提前的话只需要顺序处理就可以了啊。还可以加 Delay 随意。Reduce 或者直接 for await 不行么 哈哈哈。
        11
    knva   63 天前
    要么 Promise 要么 async/await 混用头一次见
        12
    sevenzhou1218   63 天前
    async 返回本身就是 promise 啊,总觉得代码怪怪的。
        13
    muru   63 天前
    有流程控制的时候可以试试 promise chain
    [ ...Array(10).keys()].map(page => getPage(page)).reduce((pc, func) => {
    return pc.then(() => new Promise(resolve => func(resolve)), Promise.resolve());
    });
        14
    jsq2627   63 天前
    https://asciinema.org/a/dKWCuCHxZ3vkxOaifb5Rlksxj
    抛点砖。body = null 是有用的,能让内存使用减少一半,但是还是非常占用。手动触发 GC 能让内存占用维持在稳定水平
        15
    zmlq7   63 天前
    let loading = false;

    while(page<10){
    if (loading) return;

    loading = true;

    await getAll();
    loading = false;
    }
        16
    withoutxx   63 天前
    有老哥指点一下 Promise 和 async/await 怎么一起使用吗, 上面的回复让我看懵了
        17
    yuankui   63 天前
    为啥不用循环,要用地递归来写一个自己若干天之后都不能理解的代码
        18
    FrankHB   63 天前
    在一个没法 reify 活动记录确保显式释放又没 proper tail call 保证的玩意儿里瞎搞?想多了。
    呵、呵:
    https://github.com/nodejs/CTC/issues/3
    有点意义的例子:
    https://srfi.schemers.org/srfi-45/srfi-45.html
        19
    islxyqwe   63 天前
    怎么又是 new promise 又是 async 的, async 就是返回 promise 的语法啊
    递归的话肯定要尾递归的,我觉得这么写就行了
    let loading = false;
    (async () => {
    if (loading) return;
    loading = true;
    await getAll();
    loading = false;
    })()

    async function getAll(page = 1) {
    const body = await getPage(page);
    bigHandle(body); //body 很大 处理完需要及时释放掉
    if (page < 10) {
    return getAll(++page)
    }
    }
        20
    IamUNICODE   63 天前
    不要用递归,展开吧。。
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   932 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 24ms · UTC 20:03 · PVG 04:03 · LAX 12:03 · JFK 15:03
    ♥ Do have faith in what you're doing.