使用then()返回值或Promise.resolve的区别是什么?

397

什么是以下两者的区别:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return "bbb"; // directly returning string
  })
  .then(function(result) {
    console.log(result);
  });

以及这个:

new Promise(function(res, rej) {
    res("aaa");
  })
  .then(function(result) {
    return Promise.resolve("bbb"); // returning a promise
  })
  .then(function(result) {
    console.log(result);
  });

我问这个问题是因为我在使用Angular和$http服务的链式.then()时得到了不同的行为。由于代码有点多,因此首先是上面的示例。


2
你观察到了什么“不同的行为”?这两个例子都应该能够正常工作且表现出大致相同的行为。第二个例子中的 Promise.resolve() 是不必要的。 - JLRishe
7
then 处理程序返回 Promise 没有任何问题,事实上,这是 Promise 规范的一个关键方面,您可以这样做。 - user663031
请注意,此操作适用于任意嵌套的 then - 其他语言中称之为 then 既是 map 又是 flatMap - Benjamin Gruenbaum
2
为什么第二行需要调用 res("aaa"),直接返回 "aaa" 不足以满足要求吗?对于 resolve(),为何不能像对于 reject() 一样使用 Promise catch 来处理异常? - Sam Liddicott
2
@SamLiddicott有同样的问题,而我的问题稍微复杂一些: new Promise((res, rej) => { return fetch('//google.com').then(() => { return "haha"; }) }).then((result) => alert(result)); 这段代码将会一直挂起(永远不会被解决)。但是如果我把 return "haha"; 改成 return res("haha"); 那么它就有用了,并触发警报弹窗“haha”。难道fetch() .then()没有已经把“haha”包装进一个已解决的promise吗? - Shawn
3
如果你使用Promise构造函数,应该调用传递的函数的res参数,而不是返回结果。在Promise构造函数中返回任何东西都将被忽略。 - attempt0
6个回答

201
简而言之,在then处理函数中:

A) 当x是一个值(数字,字符串等):

  1. return x 等同于 return Promise.resolve(x)
  2. throw x 等同于 return Promise.reject(x)

B) 当x是已经处理完毕的Promise(不再处于待定状态):

  1. 如果Promise已被解析,则return x 等同于 return Promise.resolve(x)
  2. 如果Promise已被拒绝,则return x 等同于 return Promise.reject(x)

C) 当x是待处理的Promise时:

  1. return x 将返回一个待定的Promise,并将在后续的then中进行评估。

请阅读Promise.prototype.then()文档以了解更多关于此主题的信息。


1
【B) 2.】应该改为“throw x”吗? - Lancer.Yan
2
@Lancer.Yan 并不是这样的,B 的意思是return语句的行为会根据 Promise 的返回值而改变。在编写代码时,你并不知道它会最终是 resolved 还是 rejected。当然,你可以使用 try-catch 来处理 rejects,然后重新抛出异常,但那是一种不同的情况。 - Arian Acosta
1
这是我见过的规范最好的摘要。 - Jibreel Keddo

153
规则是,如果在then处理程序中的函数返回一个值,则Promise将使用该值解析/拒绝,如果函数返回一个Promise,则下一个then子句将成为函数返回的Promise的then子句,因此,在这种情况下,第一个示例按正常的thens序列进行,像预期的那样打印出值,而在第二个示例中,当链接时调用Promise.resolve("bbb")的Promise对象是调用的then(出于所有目的)。它实际上的工作方式在下面更详细地描述。
从Promises/A +规范引用:
承诺解决程序是一个抽象操作,它以承诺和一个值作为输入,我们将其表示为[[Resolve]](promise, x)。如果x是 thenable,它会尝试使承诺采用x的状态,假设x至少有一些像承诺一样的行为。否则,它将用值x来实现承诺。
对thenable的处理允许Promise实现进行互操作,只要它们公开符合Promises / A + 的then方法即可。这也允许Promises/A+实现"同化"非符合实现具有合理then方法的承诺。
需要注意的关键是这行代码:
如果x是承诺,则采用其状态[3.4]
链接:https://promisesaplus.com/#point-49

5
“Adopt its state”是一种简洁实用的表述,用来表示当一个then处理程序返回一个Promise时的行为。规范参考加一分。 - user663031
80
实际上,这里规范的相关部分是[[Resolve]]被用于thenable和值,因此它实际上使用Promise包装一个值,所以return "aaa"return Promise.resolve("aaa")相同,而return Promise.resolve("aaa")return Promise.resolve(Promise.resolve("aaa"))相同。由于resolve是幂等的,多次对同一个值调用它具有相同的结果。 - Benjamin Gruenbaum
11
在thenable中,return "aaa"和return Promise.resolve("aaa")是否可以互换使用? - CSnerd
13
是的,那正是它的意思。 - Benjamin Gruenbaum
如果在then处理程序中的函数返回一个值,那么Promise将使用该值来解决/拒绝。我认为答案应该澄清Promise何时解决和何时拒绝,而不是说“Promise解决/拒绝”。 - elMeroMero

105

你的两个示例应该表现得几乎相同。

.then()处理程序内返回的值将成为从.then()返回的Promise的解析值。如果在.then()内返回的值是一个promise,那么.then()返回的promise将“采用”该promise的状态,并像返回的promise一样解决/拒绝。

在您的第一个示例中,在第一个then()处理程序中返回"bbb",因此"bbb"将传递到下一个then()处理程序。

在您的第二个示例中,您返回一个立即解析为值"bbb"的promise,因此"bbb"将传递到下一个then()处理程序。(这里的Promise.resolve()是不必要的)。

结果是相同的。

如果您可以向我们展示一个实际上表现不同的示例,我们可以告诉您发生这种情况的原因。


1
很好的回答!那么Promise.resolve();return;有什么区别? - FabianTe
3
@FabianTe 这些也会产生相同的效果,只不过使用的是 undefined 而不是 "bbb" - JLRishe

54

你已经得到一个很好的正式答案了。我想补充一下简短的回答。

以下内容与 Promises/A+ 承诺相同:

  • 调用 Promise.resolve (在您的 Angular 情况下,这是 $q.when
  • 调用promise构造函数并在其解析器中解析。在您的情况下,这是 new $q
  • then 回调中返回值。
  • 在数组上调用Promise.all并提取该值。

因此,对于承诺或普通值X,以下内容都相同:

Promise.resolve(x);
new Promise(function(resolve, reject){ resolve(x); });
Promise.resolve().then(function(){ return x; });
Promise.all([x]).then(function(arr){ return arr[0]; });

毫不奇怪,Promise规范基于Promise解决程序,这使得库之间(例如$q和本机promise)的互操作变得轻松,并使您的生活更加轻松。每当可能发生promise解决时,就会出现一个解决方案,从而创建整体一致性。


我可以问一下在做 Promise.resolve().then(function(){ return x; }); 的意义是什么吗?我发现一个类似的片段(它在 then 块内调用了一个函数)。我认为这更像是做一个超时,但速度要快一些。 http://jsben.ch/HIfDo - Sampgun
在99.99%的情况下,这与Promise.resolve(x)是相同的,没有任何意义。(0.001%的情况是我们在一个带有x属性访问器的对象或代理的with块中抛出异常。在这种情况下,Promise.resolve(x)会导致抛出错误,但是Promise.resolve().then(function(){ return x; });将是一个拒绝的承诺,因为错误是在then中抛出的)。 - Benjamin Gruenbaum
你链接了一个空的 Blitz,或者你没有保存。 无论如何,我不是在谈论语句之间的差异。我确切地说的是我写的内容。 为了更清楚,这是我所说的片段: if (validator) { Promise.resolve().then(() => { this._cdRef.markForCheck(); }); }。在这里,Promise 没有被分配,那么有什么意义呢?超时会产生(或多或少)相同的效果,不是吗? - Sampgun
1
它在所有同步代码发生之后但在任何I/O发生之前异步执行调用。这被称为“微刻语义”。 - Benjamin Gruenbaum

2
唯一的区别在于当你使用return Promise.resolve("bbb")时,会创建一个不必要的promise。从onFulfilled()处理程序返回一个promise会启动promise resolution。这就是promise chaining的工作原理。请注意,保留了HTML标签,但不包括解释。

0

仅仅是一个小点。在调试器中,可以看到'resolve'函数并不会返回。如果它是最后一条语句,它似乎会返回。如果下面还有更多的语句,'resolve'将设置Promise为已完成状态,但继续执行。

正如您在这里所读到的:'为什么JavaScript ES6 Promise在resolve之后继续执行?'

对我来说,这非常令人困惑,因为大多数示例都没有解释清楚。所以从现在开始,我必须记住使用'return resolve(value)'或者'if resolve(value) else ...other code',或者只是将'resolve'作为最后一条语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接