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

2019-10-12 10:27:55 +08:00
 lqzhgood

先上代码

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 节点 夜间模式阅读会更舒服 日间模式 太黑了。。

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

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

这代码没治了,从头重写吧
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
2019-10-12 10:44:22 +08:00
写 node 从不关心内存占用……感觉是伪命题。

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

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

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

难道只能回到 回调地狱 来处理了么~
jifengg
2019-10-12 10:47:31 +08:00
同意 @mcfog 说的。
getAll 完全可以不用 Promise,也不要用递归。里面写个 for 循环就好了。有了 await /async,完全可以把 node 异步当成同步来开发。
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
2019-10-12 10:52:14 +08:00
emmm,Page 数量是提前可以知道的么? 提前的话只需要顺序处理就可以了啊。还可以加 Delay 随意。Reduce 或者直接 for await 不行么 哈哈哈。
knva
2019-10-12 11:05:32 +08:00
要么 Promise 要么 async/await 混用头一次见
sevenzhou1218
2019-10-12 11:20:14 +08:00
async 返回本身就是 promise 啊,总觉得代码怪怪的。
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
2019-10-12 13:47:42 +08:00
https://asciinema.org/a/dKWCuCHxZ3vkxOaifb5Rlksxj
抛点砖。body = null 是有用的,能让内存使用减少一半,但是还是非常占用。手动触发 GC 能让内存占用维持在稳定水平
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
2019-10-12 14:24:28 +08:00
有老哥指点一下 Promise 和 async/await 怎么一起使用吗, 上面的回复让我看懵了
yuankui
2019-10-12 14:33:08 +08:00
为啥不用循环,要用地递归来写一个自己若干天之后都不能理解的代码
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
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
2019-10-12 14:58:10 +08:00
不要用递归,展开吧。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/608509

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX