先上代码
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
lllllliu 2019-10-12 10:34:26 +08:00
为什么不直接 Promise.all 展开呢。并没有看到或者说明下一次 Promise 需要前面做什么。如果需要前者可以链式调用呀,可以及时释放。
|
2
lqzhgood OP @lllllliu 因为第二页需要第一页的数据,所以需要递归调用。 而且需要控制频率,所以不能 Promise.all 并发一把梭
|
3
ahsjs 2019-10-12 10:42:56 +08:00
把 body 变量放外面?
|
4
mcfog 2019-10-12 10:43:53 +08:00 via Android
promise 没学明白就拿 async await 来写代码就这样了
用 new promise 来包别的 promise 已经是反模式了,还再加上 async await 这代码没治了,从头重写吧 |
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) => { //... }) . ``` |
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); } ``` 为啥想不开要递归…… |
7
lqzhgood OP @ahsjs 感觉不是单纯的 body 问题。body 再怎么大也就 1m 的纯文本。内存几十兆几十兆的涨。
主要是 Promise 整个堆栈没有释放,这个才是内存爆炸的主要原因。 但是 递归的 Promise 怎么合理的释放上一个 Promise 感觉这是个悖论了…… 所以来问问有没有这类问题的最佳实践。 难道只能回到 回调地狱 来处理了么~ |
8
jifengg 2019-10-12 10:47:31 +08:00
同意 @mcfog 说的。
getAll 完全可以不用 Promise,也不要用递归。里面写个 for 循环就好了。有了 await /async,完全可以把 node 异步当成同步来开发。 |
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; })() |
10
lllllliu 2019-10-12 10:52:14 +08:00
emmm,Page 数量是提前可以知道的么? 提前的话只需要顺序处理就可以了啊。还可以加 Delay 随意。Reduce 或者直接 for await 不行么 哈哈哈。
|
11
knva 2019-10-12 11:05:32 +08:00
要么 Promise 要么 async/await 混用头一次见
|
12
sevenzhou1218 2019-10-12 11:20:14 +08:00
async 返回本身就是 promise 啊,总觉得代码怪怪的。
|
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()); }); |
14
jsq2627 2019-10-12 13:47:42 +08:00
https://asciinema.org/a/dKWCuCHxZ3vkxOaifb5Rlksxj
抛点砖。body = null 是有用的,能让内存使用减少一半,但是还是非常占用。手动触发 GC 能让内存占用维持在稳定水平 |
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; } |
16
withoutxx 2019-10-12 14:24:28 +08:00
有老哥指点一下 Promise 和 async/await 怎么一起使用吗, 上面的回复让我看懵了
|
17
yuankui 2019-10-12 14:33:08 +08:00
为啥不用循环,要用地递归来写一个自己若干天之后都不能理解的代码
|
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 |
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) } } |
20
IamUNICODE 2019-10-12 14:58:10 +08:00
不要用递归,展开吧。。
|