在 JavaScript ES6 中,thenable 的接受如何解决和拒绝?
Posted
技术标签:
【中文标题】在 JavaScript ES6 中,thenable 的接受如何解决和拒绝?【英文标题】:In JavaScript ES6, how can a thenable accept resolve and reject? 【发布时间】:2019-12-26 19:47:02 【问题描述】:我认为到目前为止我采取的一个原则是:
promise 是一个 thenable 对象,因此它接受消息
then
,或者换句话说,一些代码可以调用这个对象上的then
方法,它是接口的一部分,带有一个实现处理程序,这是“下一步要采取的步骤”,以及拒绝处理程序,这是“如果没有成功,下一步要采取的措施”。通常最好在fulfillment handler中返回一个新的promise,这样其他代码就可以“链”上它,也就是说,“我也会告诉你下一步的动作,如果你失败了下一步的动作,所以当你完成后调用其中一个。”
然而,在a javascript.info Promise blog page 上,它说实现处理程序可以返回任何“thenable”对象(这意味着类似 promise 的对象),但是这个 thenable 对象的接口是
.then(resolve, reject)
这与通常的代码不同,因为如果一个履行处理程序返回一个新的承诺,这个 thenable 对象有接口
.then(fulfillmentHandler, rejectionHandler)
所以该页面上的代码实际上得到了resolve
并调用resolve(someValue)
。如果fulfillmentHandler
不只是resolve
的另一个名字,那么为什么这个thenable 不一样呢?
该页面上的thenable代码:
class Thenable
constructor(num)
this.num = num;
then(resolve, reject)
alert(resolve); // function() native code
// resolve with this.num*2 after the 1 second
setTimeout(() => resolve(this.num * 2), 1000); // (**)
new Promise(resolve => resolve(1))
.then(result =>
return new Thenable(result); // (*)
)
.then(alert); // shows 2 after 1000ms
【问题讨论】:
这只是使用了一对令人困惑的名称。含义没有什么不同,then
的实现对于现实世界的 thenable 来说并不是一个好主意(它不像一个承诺)。
另请注意,在原生 JavaScript 中无法测试接口。因此,只要一个对象具有 then
方法,它就会作为 thenable 传递。该方法甚至不必声明参数。它可以为所欲为。这是一个thenable。
另请注意,在 Promise 上调用 resolve
不会直接调用 fullfillmentHandler
。
我们的意思是then(fulfillmentHandler, rejectionHandler)
和then(resolve, reject)
是同一个东西吗?所以fulfillmentHandler
实际上是resolve
? (或resolveHandler
?)我把它当作new Promise(function(f, g) ...
,JS引擎立即调用这个执行器并传入一些本机代码来解析f
或resolve
......我们从不使用@调用then(f, g)
987654344@ 是resolve
是的。您也可以附加多个fullfilmentHandlers
,但承诺resolve
s 只能附加一次。
【参考方案1】:
在
let p2 = p1.then( onfulfilled, onrejected)
其中p1
是一个Promise 对象,对p1
的then
调用返回一个promise p2
并在p1
内部保存的列表中记录4 个值:
onfulfilled
的值,
创建p2
时传递给执行程序的resolve
函数的值——我们称之为resolve2
,
onrejected
的值,
在创建p2
时传递给执行程序的reject
函数的值——我们称之为reject2
。
1. 和 3. 具有默认值,因此如果省略,它们会将满足的 p1
值传递给 p2
,或使用分别为p1
的拒绝原因。
2. 或 4.(p2
的解析和拒绝函数)在内部保存,用户 JavaScript 无法访问。
现在让我们假设p1
已(或已经)通过调用resolve
函数以非thenable 值传递给其执行程序来完成。本机代码现在将搜索 p1
的现有 onfulfilled
处理程序,或处理添加的新处理程序:
每个onfulfilled
处理程序(上面的1)都从try/catch
块内的本机代码执行,并监控其返回值。如果返回值(称为v1
)是不可调用的,则以v1
作为参数调用p2
的resolve
,并沿链继续处理。
如果 onfulfilled
处理程序抛出,p2
被拒绝(通过调用上面的 4)并抛出错误值。
如果onfulfilled
处理程序返回一个thenable(promise 或promise 类对象),我们称它为pObject
,pObject
需要设置,将其稳定状态和值传递给上面的p2
。
这是通过调用来实现的
pObject.then( resolve2, reject2)
因此,如果pObject
满足,则其非thenable 成功值用于解析p2
,如果它拒绝,则其拒绝值用于拒绝p2
。
博客文章使用参数名称定义其 thenable 的 then
方法,该方法基于它在博客示例中的使用方式(解决或拒绝先前通过本机承诺调用 then
返回的承诺)。原生 promise resolve
和 reject
函数是用原生代码编写的,这解释了第一条警报消息。
【讨论】:
不错的分析。所以我认为是的,因为 promise 系统也是用 JS 编写的,我们可以推断出在某些时候,pObject.then( resolve2, reject2)
必须被调用,因此可以利用这个事实吗?在某种程度上,这就像挖掘黑匣子,总结出它的一些属性并加以利用。我认为在这种情况下这是有道理的。如果某些人认为抽象(黑盒)不是您应该猜测的东西,那么可能不推荐这种做法。
如果一种编程语言内置了 Promise 并且是由一些关键字或语法完成的,那么发生的事情就可以通过“魔术”发生。 then(resolve, reject)
的使用增加了一些混乱,因为它不是通常的接口:resolve, reject
提供给传递给 promise 构造函数的函数,而不是提供给 then()
。但我想下次我们看到then(resolve, reject)
时,我们可以考虑有人将resolveHandler 作为fulfillmentHandler 传递,以解决某些问题。
@nopole 在引入 ECMAScript 2015(又名 ES6)之前,所有的 Promise 都是用 JavaScript 编写的,通常作为更大库的一部分。其中一些库仍在使用中。当然,所有旧浏览器的 Promise pollyfills 也是用 JavaScript 编写的。原生 Promise 实现很可能是用原生代码编写的。 Promise 的一个主要设计目标是对用户隐藏 Promise 逻辑的实现。我从了解 Promise 的实际工作原理中受益匪浅,但它们的明确设计是为了防止从用户代码中改变其设计行为。
@nopole Techniclly,在 2019 年 ECMAScript 标准中,使用 Promise 解决 Promise 达到了将 PromiseResolveThenableJob 排队。根据链接的第 2 步和以下注释,调用 thenable 的 then
方法来解析或拒绝 p2
。我认为这可能是博客命名参数的运行“PromiseResolveThenableJob”的性质。另请参阅 A+ promises 规范以了解更早的承诺解决方案。【参考方案2】:
写下整个解释后,简短的回答是:这是因为 JS promise 系统传入了 resolve
和 reject
作为 fulfillmentHandler
和 rejectionHandler
。在这种情况下,所需的fulfillmentHandler
是resolve
。
当我们有代码时
new Promise(function(resolve, reject)
// ...
).then(function()
return new Promise(function(resolve, reject)
// ...
);
).then(function() ...
我们可以写同样的逻辑,使用
let p1 = new Promise(function(resolve, reject)
// ...
);
let p2 = p1.then(function()
let pLittle = new Promise(function(resolve, reject)
// ...
resolve(vLittle);
);
return pLittle;
);
返回pLittle
的行为意味着:我正在返回一个promise,一个thenable,pLittle
。现在,一旦接收者收到此 pLittle
,请确保当我将 pLittle
解析为值 vLittle
时,您的目标是立即将 p2
也解析为 vLittle
,以便可链接操作可以继续。
它是怎么做到的?
它可能有一些类似的代码:
pLittle.then(function(vLittle) // ** Our goal **
// somehow the code can get p2Resolve
p2Resolve(vLittle);
);
上面的代码说:当pLittle
被vLittle
解析时,下一个动作是用相同的值解析p2
。
所以不知何故系统可以得到p2Resolve
,但是在系统内部或者“黑盒”里面,上面的函数
function(vLittle)
// somehow the code can get p2Resolve
p2Resolve(vLittle);
可能是p2Resolve
(这主要是一个猜测,因为它解释了为什么一切正常)。所以系统会
pLittle.then(p2Resolve);
记住
pLittle.then(fn)
意思
将pLittle
的解析值传递给fn
并调用fn
,所以
pLittle.then(p2Resolve);
与
相同pLittle.then(function(vLittle)
p2Resolve(vLittle)
);
和上面的** Our goal**
完全一样。
它的意思是,系统传入一个“resolve”,作为一个履行处理程序。所以在这个确切的时刻,履行处理程序和解析是同一件事。
请注意,在原始问题的 Thenable 代码中,确实如此
return new Thenable(result);
这个Thenable
不是一个promise,你不能解决它,但是由于它不是一个promise 对象,这意味着promise(如p2
)作为返回的规则被立即解决,这就是为什么会立即调用then(p2Resolve)
。
所以我认为这取决于这样一个事实,即 ES6 Promise 的内部实现将 p2Resolve
作为第一个参数传递给 then()
,这就是为什么我们可以实现任何带有第一个参数 resolve
的 thenable 和只需调用resolve(v)
。
我认为 ES6 规范很多时候都写出了确切的实现,所以我们可能会以某种方式使用它。如果任何 JavaScript 引擎的工作方式略有不同,那么结果可能会发生变化。我想在过去,我们被告知我们不应该知道黑盒内部发生了什么,也不应该指望它是如何工作的——我们应该只知道接口。所以最好还是不要返回一个带有接口then(resolve, reject)
的thenable,而是返回一个使用接口then(fulfillmentHandler, rejectionHandler)
的新的、真实的promise对象。
【讨论】:
If any JavaScript engine works slightly differently, then the results can change
不。那么它不是一个有效的 ES262 引擎。
“所以最好还是不要返回一个有接口的thenable”,当然你可以这样做。
感觉有点hacky,但似乎可以与其他框架一起使用,有时您可以使用这样的thenable
它一点也不hacky。就是这样。
所以也许在 Promise 社区中,这是链式 Promise 的标准方式,人们认为这是理所当然的,你能说。以上是关于在 JavaScript ES6 中,thenable 的接受如何解决和拒绝?的主要内容,如果未能解决你的问题,请参考以下文章
在 ES6 深度嵌套的对象的 javascript 数组中查找值
dict 在 python 3 中传播(ES6 javascript like , ... notation)