Nodejs 请教一个 Promise 递归的最佳实践(内存释放)
Nodejs 请教一个 Promise 递归的最佳实践(内存释放)
先上代码
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 节点 夜间模式阅读会更舒服 日间模式 太黑了。。
为什么不直接 Promise.all 展开呢。并没有看到或者说明下一次 Promise 需要前面做什么。如果需要前者可以链式调用呀,可以及时释放。
因为第二页需要第一页的数据,所以需要递归调用。 而且需要控制频率,所以不能 Promise.all 并发一把梭
把 body 变量放外面?
promise 没学明白就拿 async await 来写代码就这样了
用 new promise 来包别的 promise 已经是反模式了,还再加上 async await
这代码没治了,从头重写吧
Promise 和 async 混用感觉很奇怪…确实按#1 来说,body 在处理 getAll(page)时是不必要的。<br>getPage(page)<br>.then((body) => {<br> bigHandle(body)<br>})<br>.then(() => {<br> getAll(++page)<br>}, (error) => {<br> //...<br>})<br>.<br>
写 node 从不关心内存占用……感觉是伪命题。
但你这个可能可以这么解决。<br>var body;<br>for (var i = 1; i < 10; i ++) {<br> body = await getPage(page);<br> bigHandle(body);<br>}<br>
为啥想不开要递归……
感觉不是单纯的 body 问题。body 再怎么大也就 1m 的纯文本。内存几十兆几十兆的涨。
主要是 Promise 整个堆栈没有释放,这个才是内存爆炸的主要原因。
但是 递归的 Promise 怎么合理的释放上一个 Promise 感觉这是个悖论了……
所以来问问有没有这类问题的最佳实践。
难道只能回到 回调地狱 来处理了么~
同意 说的。
getAll 完全可以不用 Promise,也不要用递归。里面写个 for 循环就好了。有了 await /async,完全可以把 node 异步当成同步来开发。
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;
})()
emmm,Page 数量是提前可以知道的么? 提前的话只需要顺序处理就可以了啊。还可以加 Delay 随意。Reduce 或者直接 for await 不行么 哈哈哈。
要么 Promise 要么 async/await 混用头一次见
async 返回本身就是 promise 啊,总觉得代码怪怪的。
https://asciinema.org/a/dKWCuCHxZ3vkxOaifb5Rlksxj
抛点砖。body = null 是有用的,能让内存使用减少一半,但是还是非常占用。手动触发 GC 能让内存占用维持在稳定水平
let loading = false;
while(page<10){
if (loading) return;
loading = true;
await getAll();
loading = false;
}
有老哥指点一下 Promise 和 async/await 怎么一起使用吗, 上面的回复让我看懵了
为啥不用循环,要用地递归来写一个自己若干天之后都不能理解的代码
在一个没法 reify 活动记录确保显式释放又没 proper tail call 保证的玩意儿里瞎搞?想多了。
呵、呵:
https://github.com/nodejs/CTC/issues/3
有点意义的例子:
https://srfi.schemers.org/srfi-45/srfi-45.html
怎么又是 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)
}
}
不要用递归,展开吧。。
在Node.js中处理Promise递归时,确保内存释放的最佳实践涉及正确管理异步链,避免内存泄漏和未处理的Promise。以下是一个使用Promise递归处理异步操作(如读取文件或进行API调用)时内存管理的示例。
function recursiveFetch(url, depth = 0, maxDepth = 5) {
if (depth > maxDepth) return Promise.resolve();
return fetch(url)
.then(response => {
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
})
.then(data => {
console.log(`Depth ${depth}:`, data);
// 假设每个响应对象中有一个 'next' URL 用于递归
if (data.next) {
return recursiveFetch(data.next, depth + 1, maxDepth);
}
})
.catch(error => {
console.error('Error:', error);
})
.finally(() => {
// 在每个递归层次结束时清理资源(如果有必要)
// 例如:clearTimeout(someTimeoutId); 或其他资源释放操作
console.log(`Depth ${depth} finished`);
});
}
// 开始递归调用
recursiveFetch('https://api.example.com/data');
在上述代码中:
fetch
函数用于模拟异步请求。- 递归深度通过
depth
参数控制,并在达到maxDepth
时停止。 finally
块确保无论成功还是失败,都会在递归的每一层结束时执行,用于资源清理(如本例中的日志记录)。- 错误处理通过
catch
块实现,避免未处理的Promise拒绝。
这样设计递归Promise可以确保内存有效管理,避免泄漏。