为啥 Promise 构造函数需要一个在完成时调用 'resolve' 的函数,但 'then' 不需要 - 它返回一个值?
Posted
技术标签:
【中文标题】为啥 Promise 构造函数需要一个在完成时调用 \'resolve\' 的函数,但 \'then\' 不需要 - 它返回一个值?【英文标题】:Why does the Promise constructor require a function that calls 'resolve' when complete, but 'then' does not - it returns a value instead?为什么 Promise 构造函数需要一个在完成时调用 'resolve' 的函数,但 'then' 不需要 - 它返回一个值? 【发布时间】:2015-09-28 05:16:09 【问题描述】:当我投入研究Promise
s 时,我对以下我没有发现讨论的问题的理解已经停止(我发现的只是Promise
构造函数和Promise
'then
的具体讨论' 功能 - 但不是比较它们的设计模式的讨论)。
1. Promise
构造函数
From the MDN documentation,我们使用了 Promise 构造函数(添加了我的评论):
new Promise(function(resolve, reject) ... ); // <-- Call this Stage 1
具有两个参数
resolve
和reject
的函数对象。首先 参数实现了承诺,第二个参数拒绝了它。我们可以 一旦我们的操作完成,调用这些函数。
2。 then
函数
转到可以在 Promise
对象上调用的 then
函数(它返回一个新的 Promise
对象),我们有 the following function signature as described by the documentation(添加了我的 cmets) :
p.then(onFulfilled, onRejected);
链接
因为
then
方法返回一个Promise,你可以很容易地链接然后 来电。
var p2 = new Promise(function(resolve, reject)
resolve(1); // <-- Stage 1 again
);
p2.then(function(value)
console.log(value); // 1
return value + 1; // <-- Call this Stage 2
).then(function(value)
console.log(value); // 2
);
我的问题
从上面的代码 sn-p 中,我似乎很清楚 在第 1 阶段传递给 resolve
函数的值(在第二次出现 resolve
- 下面 (2) , 上面) 被传递到下一个阶段(第一个 then
函数在相同的代码 sn-p 之后)。 在第 1 阶段没有返回值。但是,在第 2 阶段的返回值被传递到之后的下一个阶段(第二个then
函数)。
在用于创建Promise
的设计模式与在现有承诺上使用then
函数(也返回Promise
)之间是否缺乏对应关系,只是历史上的侥幸(一个需要调用回调但不返回任何内容,另一个返回值但不调用回调)?
或者我是否遗漏了 Promise
构造函数使用与 then
函数不同的设计模式的根本原因?
【问题讨论】:
感谢您提出这个问题。它也困扰着我:Promise 构造函数和then
方法看起来很相似......它们如何比较和对比?
感谢您提出这个问题,这是一个非常基本的概念,它不应该需要任何心理体操来理解
【参考方案1】:
Bergi's answer 非常好,对我很有帮助。这个答案是对他的补充。为了可视化Promise()
构造函数和then()
方法之间的关系,我创建了这个图表。我希望它可以帮助某人……甚至是我,几个月后。
这里的主要思想是传递给Promise()
构造函数的“执行程序”函数将任务设置为运动,将设置承诺的状态;而您传递给then()
的处理程序将对promise 的状态做出反应。
(代码示例改编自Jake Archibald's classic tutorial。)
这是对事物运作方式的高度简化视图,省略了许多重要细节。但我认为,如果一个人能够很好地掌握预期目的,这将有助于避免在进入细节时产生混淆。
一些选定的细节
立即调用执行器
一个重要的细节是传递给Promise()
构造函数的执行器函数被立即调用(在构造函数返回promise之前);而传递给then()
方法的处理函数要到稍后(如果有的话)才会被调用。
Bergi 提到了这一点,但我想在不使用术语 a/synchronously 的情况下重述它,如果您不仔细阅读可能会感到困惑:calling 函数与异步调用的区别。 被异步调用在通信中很容易被掩盖。
resolve()
不是 onFulfill()
我想强调的另一个细节,因为它让我困惑了一段时间,是 resolve()
和 reject()
回调传递给 Promise()
构造函数的执行器函数不是回调后来传递给then()
方法。回想起来,这似乎很明显,但明显的联系让我在圈子里转了太久。肯定有联系,但它是松散的、动态的。
相反,resolve()
和 reject()
回调是由“系统”提供的函数,并在您创建 Promise 时由 Promise
构造函数传递给执行器函数。当resolve()
函数被调用时,系统代码会被执行,这可能会改变promise 的状态并最终导致onFulfilled()
回调被异步调用。不要将调用resolve()
视为调用onFulfill()
的紧密包装!
【讨论】:
很好的问题@DanNissenbaum 和超级有用的答案 Lars。你的最后一段让我很头疼。谢谢。 @LarsH:我想要更多!您能否指出我在哪里可以阅读有关 Promise 在内部如何工作的信息,尤其是在打扮成 async/await 时?我有一个问题:如果我不完全理解某件事,我会感到不自在。我已经阅读了大量的博客文章,但仍然无法休息。您似乎已经掌握了要点;感谢您的慷慨分享! @Juan:我理解在使用之前真正掌握某些东西的愿望——这通常可以避免很多耗时的混乱。但是,我认为我没有更多可以提供给您的了。当我写这个答案时,我试图让我的注意力集中在 Promise 上,但我最终没有太多地使用它们,现在我已经忘记了我学到的一些东西。我想如果你想很好地理解 Promise,developer.mozilla.org/en-US/docs/Web/javascript/Guide/… 是一个很好的阅读来源。我对异步/等待一无所知。 @LarsH:谢谢拉尔斯。我已经阅读了 MDN 文档,但并不满意。我一直在阅读和编程,最终我可能能够掌握这一切。再次感谢!Don't think of calling resolve() as being a tight wrapper for calling onFulfill()!
这对我有帮助,非常感谢。【参考方案2】:
Promise
构造函数和then
方法之间没有对应关系,因为它们是两个独立的东西,设计用于不同的目的。
Promise
构造函数仅用于promisifying1 异步函数。实际上,正如您所说,它是基于 invoking resolve
/reject
回调 异步发送 值,在这种情况下没有返回值。 p>
Promise
构造函数本身确实采用了这个“解析器”回调(它同步传递resolve
和reject
)实际上是对旧的延迟模式的增强,并且与then
回调无意相似。
var p = new Promise(function(res, rej) | var def = Promise.Deferred();
setTimeout(res, 100); | setTimeout(def.resolve, 100);
); | var p = def.promise;
相比之下,then
回调是经典的异步回调,您可以使用 additional feature 从中获取 return
。它们被异步调用以接收值。
p.then(function(val) … );
总结差异:
Promise
是构造函数,而 then
是方法
Promise
需要一个回调,而then
最多需要两个
Promise
同步调用其回调,而then
异步调用其回调
Promise
总是调用它的回调,then
可能不会调用它的回调(如果承诺没有实现/拒绝)
Promise
将解析/拒绝 Promise 的功能传递给回调,then
传递调用它的 Promise 的结果值/拒绝原因
Promise
调用其回调以执行副作用(调用 reject
/resolve
),then
为其结果值调用其回调(用于链接)
是的,两者都返回承诺,尽管它们与许多其他函数(Promise.resolve
,Promise.reject
,fetch
,...)共享该特征。事实上,所有这些都基于 Promise
构造函数提供的相同的承诺构造和解析/拒绝功能,尽管这不是它们的主要目的。 then
基本上提供了将 onFulfilled
/onRejected
回调附加到现有承诺的能力,这与 Promise
构造函数完全相反。
两者都使用回调只是巧合 - 不是历史上的侥幸,而是语言功能的共同适应。
1:理想情况下,您永远不需要这个,因为所有本机异步 API 都会返回 Promise
【讨论】:
我以为then
函数也传递了一个异步函数——即异步执行的函数,就像传递给Promise
构造函数的函数异步执行一样?
@DanNissenbaum:不,如果then
采用一个(或两个)本身是异步的回调函数,那么那些需要return 承诺。
@DanNissenbaum:不,它们绝对不相似。 Promise
构造函数用于从无到有创建承诺,then
方法用于从现有承诺中获取值。
@Dan 大约一年后是接受这个出色回应的好时机。
@DanNissenbaum:我想知道这次讨论是否在措辞上磕磕绊绊。当Bergi 说then
回调是“异步调用” 时,这很明显意味着这些回调不是立即调用(当then()
执行时)而是“稍后”调用。但是当你说“传递给Promise
构造函数的函数执行异步”时,你希望不是同一意思,因为那个('executor') 函数会立即执行。也许您的意思是执行器函数通常会启动异步任务,这些任务稍后会调用resolve
或reject
?【参考方案3】:
受先前答案的启发(我将解决最令我困惑的部分):
Promise 构造函数中的 resolve
和 reject
参数不是您定义的函数。将它们视为您可以嵌入到异步操作代码中的钩子(通常您 resolve
带有成功响应和 reject
带有失败原因),因此 javascript 有一种方法最终将 Promise 标记为 Fulfilled 或 Rejected 取决于异步操作的结果;一旦发生这种情况,您在 then(fun1, fun2)
中定义的相应函数将被触发以使用 Promise(fun1(success_response)
或 fun2(failure_reason)
,取决于 Promise 是否被履行/拒绝)。由于fun1
和fun2
是普通的旧javascript 函数(它们恰好将异步操作的未来结果作为参数),它们的return
值(如果您没有明确返回,则可以是undefined
) .
另请参阅 Mozilla 的精彩文章:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
【讨论】:
我花了几个小时试图了解神秘的“功能”解决和拒绝的定义......没有人解释它。谢谢!【参考方案4】:promise 构造器执行函数的全部意义在于将 resolve 和 reject 函数传播到不使用 Promise 的代码,将其包装并将其转换为使用 Promise。如果您只想将其限制为同步函数,那么是的,可以使用函数的返回值,但这很愚蠢,因为有用的部分是将解析器和拒绝函数传递给稍后实际运行的代码(返回后的方式),例如传递给某些异步 API 的回调。
【讨论】:
【参考方案5】:这是Promise的执行流程。
var p = new Promise((resolve, reject) =>
console.log("1");
resolve("OK");
);
//The above code creates a promise and execustion starts immediately.
//it happens aynchronously. So the execution will not be blocked.
//Promise exustion will not wait for 'then' call on promise
console.log("2");
//The above line displays 2 on the console.
p.then((result)=>
console.log("3");
console.log(result);
);
//The above code shoud not block execution. So it may print 4 first
// then 3
console.log("4");
【讨论】:
以上是关于为啥 Promise 构造函数需要一个在完成时调用 'resolve' 的函数,但 'then' 不需要 - 它返回一个值?的主要内容,如果未能解决你的问题,请参考以下文章