首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在javascript -内存考虑事项中递归构建承诺链

在javascript -内存考虑事项中递归构建承诺链
EN

Stack Overflow用户
提问于 2015-04-28 17:21:28
回答 4查看 17.6K关注 0票数 58

这个答案中,承诺链是递归构建的。

略为简化,我们有:

代码语言:javascript
复制
function foo() {
    function doo() {
        // always return a promise
        if (/* more to do */) {
            return doSomethingAsync().then(doo);
        } else {
            return Promise.resolve();
        }
    }
    return doo(); // returns a promise
}

这大概会产生一个调用堆栈和一个承诺链--即“深”和“宽”。

我预计内存峰值会比执行递归或单独构建承诺链更大。

  • 是这样吗?
  • 有没有人考虑过以这种方式构建链的记忆问题?
  • 记忆消耗在承诺库之间会有所不同吗?
EN

回答 4

Stack Overflow用户

发布于 2015-04-28 17:27:46

免责声明:过早优化是不好的,找出性能差异的真正方法是对代码进行基准测试,您不应该担心这一点(我只需要做一次,我已经为至少100个项目使用了承诺)。

是这样吗?

是的,,承诺必须“记住”它们遵循的内容,如果您对10000的承诺执行此操作,您将有一个10000长的承诺链,如果不这样做,您就不会(例如,使用递归)--对于任何排队流控制都是这样。

如果你必须跟踪10000件额外的东西(操作),那么你需要为它保留内存,这需要时间,如果这个数字是一百万,它可能是不可行的。各图书馆的情况各不相同。

有没有人考虑过以这种方式构建链的记忆问题?

当然,这是一个很大的问题,也是在库中使用像Promise.each这样的东西的用例,比如蓝鸟在then上的链接。

为了避免这种风格,我个人在代码中使用了一个快速应用程序,它只遍历VM中的所有文件--但在绝大多数情况下,这是一个没有问题的问题。

记忆消耗在承诺库之间会有所不同吗?

是的,很大。例如,如果检测到承诺操作已经是异步的(例如,如果它以Promise.delay启动),并且只会同步执行(因为异步保证已经被保留),它就不会分配额外的队列。

这意味着,我在回答第一个问题时所声称的并不总是正确的(但在常规用例中是正确的):除非提供内部支持,否则本机承诺永远无法做到这一点。

再说一遍,这并不令人惊讶,因为承诺库之间的差别是数量级的。

票数 16
EN

Stack Overflow用户

发布于 2016-04-14 06:34:35

我刚刚发现了一种可能有助于解决问题的方法:不要在最后一个then中执行递归,而是在最后一个catch中执行递归,因为catch已经超出了解决链。使用您的例子,应该是这样的:

代码语言:javascript
复制
function foo() {
    function doo() {
        // always return a promise
        if (/* more to do */) {
            return doSomethingAsync().then(function(){
                        throw "next";
                    }).catch(function(err) {
                        if (err == "next") doo();
                    })
        } else {
            return Promise.resolve();
        }
    }
    return doo(); // returns a promise
}
票数 5
EN

Stack Overflow用户

发布于 2016-07-12 14:34:25

作为对现有答案的补充,我想说明这个表达式,它是这样一个异步递归的结果。为了简单起见,我使用一个简单的函数来计算给定基和指数的幂。递归和基本情况等价于OP的示例:

代码语言:javascript
复制
const powerp = (base, exp) => exp === 0 
 ? Promise.resolve(1)
 : new Promise(res => setTimeout(res, 0, exp)).then(
   exp => power(base, exp - 1).then(x => x * base)
 );

powerp(2, 8); // Promise {...[[PromiseValue]]: 256}

在某些替代步骤的帮助下,可以替换递归部分。请注意,可以在浏览器中计算此表达式:

代码语言:javascript
复制
// apply powerp with 2 and 8 and substitute the recursive case:

8 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 8)).then(
  res => 7 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 7)).then(
    res => 6 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 6)).then(
      res => 5 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 5)).then(
        res => 4 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 4)).then(
          res => 3 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 3)).then(
            res => 2 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 2)).then(
              res => 1 === 0 ? Promise.resolve(1) : new Promise(res => setTimeout(res, 0, 1)).then(
                res => Promise.resolve(1)
              ).then(x => x * 2)
            ).then(x => x * 2)
          ).then(x => x * 2)
        ).then(x => x * 2)
      ).then(x => x * 2)
    ).then(x => x * 2)
  ).then(x => x * 2)
).then(x => x * 2); // Promise {...[[PromiseValue]]: 256}

口译:

  1. 对于new Promise(res => setTimeout(res, 0, 8)),执行器将被立即调用,并执行非阻塞计算(用setTimeout模拟)。然后返回一个未确定的Promise。这与OP示例中的doSomethingAsync()相当。
  2. 通过Promise通过.then(...与此解析回调相关联。注意:这个回调的主体被powerp的主体所取代。
  3. 点2),并且构建一个嵌套的then处理程序结构,直到达到递归的基本情况。基本大小写返回用Promise解析的1
  4. 嵌套的then处理程序结构通过相应地调用关联的回调来“解除”。

为什么生成的结构是嵌套的而不是链式的?因为then处理程序中的递归大小写阻止它们返回一个值,直到达到基本大小写为止。

如果没有堆栈,这怎么能工作呢?关联的回调形成一个“链”,它连接主事件循环的连续微任务。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29925948

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档