【问题标题】:Resolved promise waits for inner promise to be resolved已解决的承诺等待内部承诺被解决
【发布时间】:2018-04-02 17:39:34
【问题描述】:

我想对一个promise有一个promise,像这样:

let first = new Promise(resolveFirst => {
    setTimeout(() => {
        resolveFirst("resolved!");
    }, 2000)
});

let second = new Promise(resolveSecond => {
    setTimeout(() => {
        resolveSecond(first);
    }, 10)
});

second.then(secondValue => {
    console.log("second value: ", secondValue);
    secondValue.then(firstValue => {
        console.log("first value: ", firstValue);
    });
});

这样console.log("second value: ", secondValue); 将在10 毫秒之后打印,然后console.log("first value: ", firstValue) 将在2000 之后打印。

但是我得到了:

第二个值:已解决!

还有一个错误:

Uncaught (in promise) TypeError: secondValue.then is not a function

2010millies 之后在一起。

似乎当第二个promise被解决并返回第一个promise时,它会自动等待第一个promise也被解决。

这是为什么呢?我该如何打破它们之间的关系?


编辑

这是使用Array.reduce()在facebook上发布的解决方案:

const runPromisesInSeries = 
    ps => ps.reduce((p, next) => 
        p.then(next), Promise.resolve());

const delay = d => new Promise(r => setTimeout(r, d));
runPromisesInSeries([() => delay(1000), () => delay(2000)]);
//executes each promise sequentially, taking a total of 3 seconds to complete

(https://www.facebook.com/addyosmaniofficial/photos/a.10151435123819601.1073741825.129712729600/10155386805609601/?type=3&theater)

【问题讨论】:

  • 你真的需要像这样返回first 承诺吗?问题是,当您调用second.then() 时,您将解析所有的承诺链(您通过从second 解析first 创建该链)。

标签: javascript promise


【解决方案1】:

按照设计,通过第二个 Promise 解决的 Promise 在第二个 Promise 被解决(已完成或被拒绝)后采用第二个 Promise 的值和状态,等待它在必要时被解决。

不可能用 promise 或 thenable 对象(任何具有then 方法的对象)值来实现 promise,除非 promise 库存在严重缺陷(旧版本的 JQuery 会用 promise 实现它们自己的 promise 对象来自不同库的对象)。

Promise 拒绝没有相同的检查,并且会很高兴地将 Promise 对象沿链传递,而无需等待它被解决。所以你可以用第一个拒绝第二个承诺并在catch 子句中选择它。

虽然技术上可行,但我强烈建议不要这样做,除非证明可以这样做 - 对于试图理解代码的第三方来说,这是一个维护问题。

虽然您可以将 Promise 作为对象属性传递到 Promise 链的成功通道中,但重新分析 Promise 组合可能会提供更好或更标准的解决方案。例如。 Promise.all 等待完成两个或多个独立的承诺,然后再继续执行共同任务。

【讨论】:

  • 我需要改变一些东西,但Promise.all 可能对我有用。
【解决方案2】:

这只是他们魅力的一部分:如果您的承诺 A 解析为 thenable B,那么 A 仅在 B 解析后才解析,并且 A 采用 B 的已解析值。这是Promises/A+ES6 的一部分,作为“承诺解决程序”的一部分。

如果(比如说)一个动作在完成之前需要一个动作(比如登录),或者需要加载另一页结果,或者有自己的重试逻辑,您就会看到这样做的一些优势。

虽然它不是很惯用,但如果你想立即返回一个未解决的承诺而不等待它,你可以通过将它传递给 {object}[array] 来实现,但可能没有太多理由:除了等待它完成之外,你会对返回的 Promise 做什么?

【讨论】:

  • 如果第二个承诺的完成需要一段时间,我想通知用户第一个承诺已经完成,我们现在正在等待第二个?
  • @NitzanTomer 如果您返回 {object},那将是您可以获得的那种粒度,但是如果您将 Promise 视为“无论何时到达时都会获得正确值的纯抽象”,而不管中间延迟/步骤”,那么您可能不想重用返回值作为通道说“仍在考虑它,这是对下一个值的承诺”。与other requests for promise "progress" 类似,您可能需要考虑一个 Observable、您设置的字段或您调用的回调函数。
  • 我可能最终会在两个承诺之间分开,然后按照@Traktor53 的建议使用Promise.all
【解决方案3】:

您只能将then 链接到一个承诺。 secondValue 不是一个承诺,它只是你调用 second 承诺时返回的值。或来自该承诺的 resolved 值。

如果你想让它工作,试试这个:

let first = new Promise(resolveFirst => {
  setTimeout(() => {
    resolveFirst("resolved!");
  }, 2000)
});

let second = new Promise(resolveSecond => {
  setTimeout(() => {
    resolveSecond(first);
  }, 10)
});

second.then(secondValue => {
    console.log("second value: ", secondValue);
    /** first is a Promise. return a promise, and the next "then"
     * will pass the result of that Promise.
     */
    return first
  })
  .then(firstValue => {
    console.log(firstValue)
  })

编辑 1

进一步解释为什么 second 承诺解决了 first 承诺的最终值 (string),而不是承诺本身 can be found on mdn:

Promise.resolve()

如果该值是一个承诺,则该对象将成为调用 Promise.resolve 的结果;否则返回的 Promise 将用该值实现。

【讨论】:

  • secondValue 确实是一个承诺,因为我像这样解析secondresolveSecond(first) 其中first 是一个承诺。
  • secondValuestring。尝试写console.log(typeof secondValue),结果将是string
  • 好的,它是一个字符串,但我希望它是承诺,这就是整个问题,也是我问这个问题的原因。
  • 查看我对答案的补充。
  • 是的,我没有使用Promise.resolve,但原因是一样的。我可能会按照@Traktor53 的建议使用Promise.all
猜你喜欢
  • 2020-08-14
  • 2015-06-24
  • 2018-03-19
  • 2019-04-29
  • 1970-01-01
  • 2021-11-26
  • 2014-10-29
  • 2014-03-12
  • 1970-01-01
相关资源
最近更新 更多