为啥 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 【问题描述】:

当我投入研究Promises 时,我对以下我没有发现讨论的问题的理解已经停止(我发现的只是Promise 构造函数和Promise 'then 的具体讨论' 功能 - 但不是比较它们的设计模式的讨论)。


1. Promise 构造函数

From the MDN documentation,我们使用了 Promise 构造函数(添加了我的评论):

new Promise(function(resolve, reject)  ... ); // <-- Call this Stage 1

具有两个参数resolvereject 的函数对象。首先 参数实现了承诺,第二个参数拒绝了它。我们可以 一旦我们的操作完成,调用这些函数。


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 构造函数本身确实采用了这个“解析器”回调(它同步传递resolvereject)实际上是对旧的延迟模式的增强,并且与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.resolvePromise.rejectfetch,...)共享该特征。事实上,所有这些都基于 Promise 构造函数提供的相同的承诺构造和解析/拒绝功能,尽管这不是它们的主要目的。 then 基本上提供了将 onFulfilled/onRejected 回调附加到现有承诺的能力,这与 Promise 构造函数完全相反。

两者都使用回调只是巧合 - 不是历史上的侥幸,而是语言功能的共同适应。

1:理想情况下,您永远不需要这个,因为所有本机异步 API 都会返回 Promise

【讨论】:

我以为then函数也传递了一个异步函数——即异步执行的函数,就像传递给Promise构造函数的函数异步执行一样? @DanNissenbaum:不,如果then 采用一个(或两个)本身是异步的回调函数,那么那些需要return 承诺。 @DanNissenbaum:不,它们绝对不相似。 Promise 构造函数用于从无到有创建承诺,then 方法用于从现有承诺中获取值。 @Dan 大约一年后是接受这个出色回应的好时机。 @DanNissenbaum:我想知道这次讨论是否在措辞上磕磕绊绊。当Bergi 说then 回调是“异步调用” 时,这很明显意味着这些回调不是立即调用(当then() 执行时)而是“稍后”调用。但是当你说“传递给Promise 构造函数的函数执行异步”时,你希望不是同一意思,因为那个('executor') 函数会立即执行。也许您的意思是执行器函数通常会启动异步任务,这些任务稍后会调用resolvereject【参考方案3】:

受先前答案的启发(我将解决最令我困惑的部分):

Promise 构造函数中的 resolvereject 参数不是您定义的函数。将它们视为您可以嵌入到异步操作代码中的钩子(通常您 resolve 带有成功响应和 reject 带有失败原因),因此 javascript 有一种方法最终将 Promise 标记为 Fulfilled 或 Rejected 取决于异步操作的结果;一旦发生这种情况,您在 then(fun1, fun2) 中定义的相应函数将被触发以使用 Promise(fun1(success_response)fun2(failure_reason),取决于 Promise 是否被履行/拒绝)。由于fun1fun2 是普通的旧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' 不需要 - 它返回一个值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在malloc中不调用构造函数? [复制]

为啥在malloc中不调用构造函数? [复制]

从函数返回对象时调用C ++中的复制构造函数?

ES6之Promise

在构造函数上启动线程时调用不匹配

如何检查 Promise 是不是处于待处理状态 [重复]