请教一个 Promise 递归的最佳实践(内存释放) - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lqzhgood
V2EX    Node.js

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

  •  
  •   lqzhgood 2019-10-12 10:27:55 +08:00 7452 次点击
    这是一个创建于 2197 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先上代码

    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 条附言    2019-10-12 11:20:04 +08:00
    page < 10 只是为了精简代码写的。 实际上这是一个状态值
    bigHandle(body) 就是在处理并提取这个状态值 来决定是不是要下一次 getAll。

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

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

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

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

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

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

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

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

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

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

    loading = true;

    await getAll();
    loading = false;
    }
    withoutxx
        16
    withoutxx  
       2019-10-12 14:24:28 +08:00
    有老哥指点一下 Promise 和 async/await 怎么一起使用吗, 上面的回复让我看懵了
    yuankui
        17
    yuankui  
       2019-10-12 14:33:08 +08:00
    为啥不用循环,要用地递归来写一个自己若干天之后都不能理解的代码
    FrankHB
        18
    FrankHB  
       2019-10-12 14:50:06 +08:00
    在一个没法 reify 活动记录确保显式释放又没 proper tail call 保证的玩意儿里瞎搞?想多了。
    呵、呵:
    https://github.com/nodejs/CTC/issues/3
    有点意义的例子:
    https://srfi.schemers.org/srfi-45/srfi-45.html
    islxyqwe
        19
    islxyqwe  
       2019-10-12 14:51:38 +08:00
    怎么又是 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)
    }
    }
    IamUNICODE
        20
    IamUNICODE  
       2019-10-12 14:58:10 +08:00
    不要用递归,展开吧。。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1256 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 17:29 PVG 01:29 LAX 10:29 JFK 13:29
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86