关于异步 Promises 的重点/难点

2022-12-18 0 742

迄今,可能每一 JavaScript 开发人员和他们的外祖母都听闻过 Promises。假如你没,所以你即Sonbhadra。Promises 的基本概念是由 CommonJS 组核心成员的核心成员在 Promises/A 规范中明确提出来的。Promises 被渐渐用于一种管理工作触发器操作形式反弹的形式,但所致它的结构设计,它相比而言这个管用得多。实际上,由于它的多种不同用语,有不计其数人说我——在我写过一些有关 Promises 的小东西后——我陈述了 Promises 的重点项目。所以什么是 Promises 的重点项目呢?

一点儿有关 Promises 的小东西

在我早已开始 Promise 的 重点项目以后,我想我应该给你一点儿它怎样工作的内貌。两个 Promise 是两个第一类——根据 Promise/A 规范化——只须要两个形式:then。 then 形式暗含四个模块:两个获得成功反弹,两个失利反弹,和两个行进反弹(规范化没要求包括行进反弹的同时实现,但是很多都同时实现了)。两个全捷伊 Promise 第一类从每一 then 的初始化中回到。

两个 promise 能是 四种状况之一:未顺利完成的,顺利完成的,或是失利的。promise 以未顺利完成的状况早已开始,假如获得成功它将会是顺利完成态,假如失利Sonbhadra是失利态。当两个 promise 终端到顺利完成态,大部份注册登记到它的获得成功反弹将被初始化,所以会将获得成功的结论值传予它。除此之外任何人注册登记到 promise 的获得成功反弹,Sonbhadra在它早已顺利完成之后立刻被调 用。

反之亦然的事出现在 promise 终端到失利态的时候,除它初始化的是失利反弹而不是获得成功反弹。对包涵行进优点的同时实现而言,promise 在它返回未顺利完成状况从前的任何人时 刻,都能预览它的progress。当 progress 被预览,大部份的行进反弹(progress callbacks)会被传达以 progress 的值,并被立刻初始化。行进反弹被以有别于获得成功和失利反弹的形式处置;假如你在一个 progress 预览已 经出现之后注册登记了两个行进反弹,捷伊行进反弹只会在它被注册登记之后被已预览的 progress 初始化。

我们不会更进一步深入细致promise状况是怎样管理工作的,因为那无此规范化内,所以每一同时实现都有差异。在前面的范例中,你Sonbhadra看见它是怎样顺利完成的,但目前这就是大部份你须要知道的。

处置反弹

像前面提到的为触发器操作形式处置反弹,是 Promises 的最基本和最普通的用途,让我们将两个标准的反弹与两个采用了 Promise 的反弹比较一下。

// Normal callback usageasyncOperation(function() { // Heres your callback});// Now `asyncOperation` returns a promiseasyncOperation().then(function(){ // Heres your callback});

我很怀疑只是看见这个范例的话是否有人会真的关心去使用promises。看起来没什么好处,除“then”使得在触发器操作形式顺利完成之后的反弹函数 被初始化这件事看起来更加明显。但是即使是这个好处,我们现在有了更多的代码(抽象应该使我们的代码更短,不是吗?)所以promise比标准反弹稍微性能 差一点儿。

但是不要让这阻碍到你。假如这就是 promise 能做的最好的事,这篇文章就不会存在了

// Normal callback usage => PYRAMID OF DOOOOOOOOMasyncOperation(function(data){ // Do some processing with `data` anotherAsync(function(data2){ // Some more processing with `data2` yetAnotherAsync(function(){ // Yay were finished! }); });});// Lets look at using promisesasyncOperation().then(function(data){ // Do some processing with `data` return anotherAsync();}).then(function(data2){ // Some more processing with `data2` return yetAnotherAsync();}).then(function(){ // Yay were finished!});

正如你所见,promises的使用使得事变扁平所以更可读了。这能起作用是因为——像早先提到的——then回到了两个promise,所以你 能将then的初始化不停的串连起来。由then回到的promise装载了由初始化回到的值。假如初始化回到了两个promise(像这个范例中的情形一 样),then回到的 promise装载了与你的反弹回到的promise所装载的相同值。这内容许多,因此我将帮助你一步一步的理解它。

触发器操作形式回到两个promise第一类。因此我们在这个promise第一类中初始化then,并且传予它两个反弹函数;then也会回到两个 promise。当触发器操作形式结束,它将给promise装上数据。然后(第一次)反弹被初始化,数据被作为模块传达进去。假如反弹不含有回到值,then返 回的promiseSonbhadra立刻不带值组装。假如反弹回到的不是两个promise,所以then回到的 promiseSonbhadra立刻装载这个数值。假如反弹回到两个promise(像范例中的),所以then回到的 promise将等待直到我们反弹回到的promise被完全装载。一旦我们反弹的 promise被装载,它装载的值(本例中就是data2)将会被提交给then的promise。然后then中的promise装载了data2。等 等。听起来有点复杂,但实际上很简单,假如我说的你不能理解,我非常抱歉。我猜我可能不是谈论它的最佳人选。

用命名的回调替代

但显然 promises 不是使这个结构扁平化的唯一形式。在写了一篇提到promises解决了问题的帖子之后,有个人对该帖评论说……

我想promises有时是管用的,但是“嵌套”的反弹的问题(圣诞树综合症)能仅用两个命名的函数作为两个模块替代匿名函数的形式平常的处置:

asyncCall( param1, param2, HandlerCallback );function HandlerCallback(err, res){// do stuff}

它的范例只是给出了一层深的范例,但它仍是正确的。我们来扩展我前面的范例,使这个看起来容易些。

命名反弹

// Normal callback usage => PYRAMID OF DOOOOOOOOMasyncOperation(handler1);function handler1(data) { // Do some processing with `data` anotherAsync(handler2);}function handler2(data2) { // Some more processing with `data2` yetAnotherAsync(handler3);}function handler3() { // Yay were finished!}

看看上面的代码!他们绝对是对的!它就是两个扁平的结构,但是这里有个问题反之亦然也存在于 我从前从来没注意过的老的反弹范例中:依赖性和复用性。依赖性和复用性是相互关联的可逆类型。一样小东西依赖的越少,所以它的复用性就越大。在以上的范例 中,handler1依赖handler2,handler2依赖handler3.这就意味着handler1无论所致任何人目的都不可在被用除非 handler2也呈现出来。假如你不打算重用他们,所以给你的函数命名又有什么意义呢?

最糟糕的的是handler1都不关心在handler2里面出现了什么事。它压根就不须要handler2除和它触发器工作。因此,让我们消除这些依赖性然后通过用promise使函数更具复用性。

asyncOperation().then(handler1).then(handler2).then(handler3);function handler1(data) { // Do some processing with `data` return anotherAsync();}function handler2(data2) { // Some more processing with `data2` return yetAnotherAsync();}function handler3() { // Yay were finished!}

这样看起来是不是好多了?假如除此之外的函数存在的话,现在handler1和handler2都互不相关了。想看看他们是否真的很棒呢?现在 handler1能被用在不须要handler2的情况下了。相反,handler1被操作形式之后,我们将能用另两个handler。

asyncOperation().then(handler1).then(anotherHandler);function handler1(data) { // Do some processing with `data` return anotherAsync();}function anotherHandler(data2) { // Do some really awesome stuff that youve never seen before. Itll impress you}

现在handler1早已从handler2脱离所以能被用在了更多的情形中,特别是那些由handler2提供的功能而我们又不想用的。这就是 复用性!评论家解决代码易读性的唯一形式就是通过消除缩进。我们不想消除缩进仅仅是为了缩进。多层次的缩进仅仅是某些事错误的标志,问题不一定在它本 身。他就像是由脱水引起的头痛。真正的问题是脱水,不是头痛。解决的形式是获得水合物,而不是用一些止痛药。

并行触发器操作形式

在前面我提到的文章里,我将promises与events在处置触发器操作形式方面做了比较。遗憾的是,按照那些曾提到过的人在评论里给的说法,我比较 的不是很获得成功。我描述出了promises的力量,接着转到events来描述它的力量,就像在我的特别项目里用到的那样。没比较和对比。一位评论者 写道(修改了一点儿语法错误):

我想用帖子中的范例是两个坏的对照。有篇论文证明了promises的值Sonbhadra怎样,假如按下虚构的“启动服务器按钮”,将不仅仅是启动两个web服务器,还有两个数据库服务器,当它都在运行的时候只是预览了UI。

使用promise的.when形式Sonbhadra使这种“多个触发器操作形式”范例变得普通,然而响应多个触发器事件须要两个并不普通的代码量。

他完全正确。实际上我没比较那两种情况。那篇文章的要点实际在于说明promises不是触发器操作形式的唯一机制,所以在一些情况下,它也不一定是最好的。在这个评论者指出的情况下,promises当然是最佳的解决办法。我们来看看他说的是什么

jQuery 具有 两个名为when的形式 ,能带上任意数量的promise模块,并回到两个单一的promise。假如任何人两个promise传入失利,when回到的promise也会失利。假如大部份的promises被装载,所以每个值都Sonbhadra按照promises被定义的顺序传达给附加的反弹。

以并行的形式执行不计其数的触发器操作形式非常管用,然后只要在它之中的每两个结束之后继续执行反弹。我们看两个简单的范例。

jQuery.when// Each of these async functions return a promisevar promise1 = asyncOperation1();var promise2 = asyncOperation2();var promise3 = asyncOperation3();// The $ refers to jQuery$.when(promise1, promise2, promise3).then( function(value1, value2, value3){ // Do something with each of the returned values });

人们经常说这是 promises 带来的最好的小东西之一,也是 promises 的一部分重要的意义所在。我也认为这是个简化了大量操作形式的好优点,但是这种 when 形式的机制 根本就没在任何人 Promises 规范化中提到,所以我不认为它是 Promises意义所在。有两个规范化提到了 when 形式,但是和上面的完全不同。就我所知,jQuery 是唯一的同时实现了这种 when 形式的库。其他的 promises 库,例如 Q, Dojo, 和 when 依照 Promises/B spec 同时实现了 when 形式, 但是并没同时实现注释者提及的 when 形式。但是,Q 库有两个 all形式,when.js 也有两个 parallel形式,与上面的 jQuery.when 形式作用一样,只是它接受两个数组类型的模块,而不是任意数量的模块。

Promise是处置以下场景的更好的形式:

“我想在这个数据库中找两个用户,但find形式是触发器的。”

因此,这里我们有了两个不能立刻回到值的find形式。但最终它确实”回到”了两个数值(通过两个反弹的形式),而你希望以某种形式处置这个数值。现在,通过使用两个反弹,你能定义两个继续部分,或是说“一些将在之后时间里处置这个数值的代码”

Promise改变了那种“嘿,这里是一些你会发现你用来处置回到数值的代码”。它是一些允许”find”形式说“嘿,我将忙着找你要找的信息,但与此同时你能继续等着回到结论,所以你能同时以任何人你希望的形式处置它,就像实际的小东西!”

Promise代表了真实的数值。那就是陷阱。它工作在你像处置实际小东西一样处置Promise的时候。Promise的JavaScript同时实现期待你给它传达两个反弹函数,这只是两个“巧合”,它不是重要的事。

我相信这真的就是promise的重点项目。为什么?读一读 Promise/A规范化 的第一句“两个promise代表了两个操作形式的一次顺利完成最终回到的数值。“使它有点明显了,是不是?好吧,即使那就是重点项目,那也不能阻止我在前面本文中呈 现其他人的见解。不管怎么说,我们再多谈论这个思想一点儿。

结论

promise的重点项目是它代表两个操作形式回到的最终结论值,但使用它的原因是使同步操作形式更好的并行。自从触发器编程进入此场景,到处都是弹出的反弹,以奇怪 的形式遮住我们的代码。Promise是一种改变其的形式。Promise允许我们以同步的形式写代码,同时给予我们代码的触发器执行。

举报/反馈

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务