【问题标题】:Does promise resolved in n-th setTimeout cause memory leak?在第 n 个 setTimeout 中解决的承诺会导致内存泄漏吗?
【发布时间】:2018-07-25 14:20:35
【问题描述】:

我可以在 Chrome 任务管理器中看到,运行以下代码的选项卡占用了越来越多的内存,并且直到 promise 解决后才会释放

更新

这里的主要思想是使用单一的“低级”方法来处理来自服务器的“忙碌”响应。其他方法只是将带有请求数据的 url 路径传递给它并等待有价值的响应。

删除了一些反模式。

var counter = 1

// emulates post requests sent with ... axios
async function post (path, data) {
    let response = (counter++ < 1000) ? { busy: true } : { balance: 3000 }
    return Promise.resolve(response)
}

async function _call (path, data, resolve) {
    let response = await post()

    if (response.busy) {
        setTimeout(() => {
            _call(path, data, resolve)
        }, 10)
        throw new Error('busy')
    }

    resolve(response.balance)
}

async function makePayment (amount) {
    return new Promise((resolve, reject) => {
        _call('/payment/create', {amount}, resolve)
    })
}

async function getBalance () {
    return new Promise((resolve, reject) => {
        _call('/balance', null, resolve)
    })
}

makePayment(500)
    .then(() => {
        getBalance()
            .then(balance => console.log('balance: ', balance))
            .catch(e => console.error('some err: ', e))
    })

【问题讨论】:

  • Don't use return await,避免Promise constructor antipattern,不要传递resolve,在最小级别承诺(setTimeout),不要抛出永远不会被捕获的错误!
  • 不确定内存,但该代码几乎包含所有的承诺反模式:p
  • 这段代码应该积累“未处理的承诺拒绝”错误,但它不应该泄漏任何自身的内存。
  • 在 10 毫秒的计时器上一遍又一遍地调用自己可能不会给垃圾收集器足够的时间来完成其正常工作。这可能是您看到内存不断增加直到整个过程完成的部分原因。
  • 仅供参考,这是您的代码的清理版本,它删除了 Promise 反模式:jsfiddle.net/jfriend00/9349nuLs

标签: javascript memory-leaks es6-promise


【解决方案1】:

您第一次在这里拨打_call()

async function getBalance () {
    return new Promise((resolve, reject) => {
        _call('/balance', null, resolve)
    })
}

它不会调用resolve回调,它会返回一个被拒绝的promise,因此你在getBalance()中的new Promise()最初不会做任何事情。请记住,由于_call 被标记为async,所以当你抛出时,它会被捕获并变成被拒绝的承诺。

当计时器触发时,它将调用resolve(),这将解决getBalance() 的承诺,但它没有值,因此您无法获得平衡。当你最终调用resolve(response.balance) 时,你已经调用了resolve() 函数,所以它所属的promise 被锁定并且不会改变它的值。


正如其他人所说,这段代码有各种各样的问题(很多反模式)。这是一个简化版本,当我在 node.js 或答案中的 sn-p 中运行它时可以使用:

function delay(t, val) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, val), t);
    });
}


var counter = 1;

function post() {
    console.log(`counter = ${counter}`);
    // modified counter value to 100 for demo purposes here
    return (counter++ < 100) ? { busy: true } : { balance: 3000 };
}

function getBalance () {

    async function _call() {
        let response = post();

        if (response.busy) {
            // delay, then chain next call
            await delay(10);
            return _call();
        } else {
            return response.balance;
        }
    }

    // start the whole process
    return _call();
}

getBalance()
    .then(balance => console.log('balance: ', balance))
    .catch(e => console.error('some err: ', e))

【讨论】:

  • 这都是真的(尽管async/await 代码可以进一步简化为使用循环而不是递归),但这并不能解释为什么 OP 会出现内存泄漏?
猜你喜欢
  • 2013-12-02
  • 2013-07-25
  • 2011-02-28
  • 2021-03-23
  • 2021-09-25
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多