Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung der asynchronen Issues_Javascript-Fähigkeiten von Promises

Detaillierte Erläuterung der asynchronen Issues_Javascript-Fähigkeiten von Promises

WBOY
WBOYOriginal
2016-05-16 15:32:281302Durchsuche

Mittlerweile hat wahrscheinlich jeder JavaScript-Entwickler und seine Großmutter von Promises gehört. Wenn nicht, werden Sie es gleich tun. Das Konzept der Versprechen wurde von Mitgliedern des CommonJS-Teams in der Promises/A-Spezifikation vorgeschlagen. Versprechen werden zunehmend zur Verwaltung von Rückrufen für asynchrone Vorgänge verwendet, sind aber aufgrund ihres Designs weitaus nützlicher. Tatsächlich haben mir unzählige Menschen aufgrund ihrer vielfältigen Verwendungsmöglichkeiten gesagt, nachdem ich etwas über Versprechen geschrieben habe, dass ich den Sinn von Versprechen „verpasse“. Was ist also der Sinn von Versprechen?

Etwas über Versprechen

Bevor ich mich mit den Einzelheiten der Versprechen befasse, möchte ich Ihnen einen kleinen Einblick in ihre Funktionsweise geben. Ein Promise ist ein Objekt, das gemäß der Promise/A-Spezifikation nur eine Methode benötigt: then. Die then-Methode benötigt drei Parameter: einen Erfolgsrückruf, einen Fehlerrückruf und einen Vorwärtsrückruf (die Spezifikation erfordert nicht die Einbeziehung der Implementierung von Vorwärtsrückrufen, viele tun dies jedoch). Von jedem then-Aufruf wird ein neues Promise-Objekt zurückgegeben.
Ein Versprechen kann einen von drei Zuständen haben: unerfüllt, abgeschlossen oder fehlgeschlagen. Ein Versprechen beginnt in einem unerfüllten Zustand. Wenn es erfolgreich ist, wird es erfüllt, wenn es fehlschlägt, ist es gescheitert. Wenn ein Versprechen in den Abschlussstatus wechselt, werden alle dafür registrierten Erfolgsrückrufe aufgerufen und der Erfolgsergebniswert wird an das Versprechen übergeben. Darüber hinaus wird jeder für das Versprechen registrierte Erfolgsrückruf sofort nach dessen Abschluss aufgerufen.

Das Gleiche passiert, wenn das Versprechen in den Status „Fehlgeschlagen“ wechselt, außer dass es den Rückruf „Fehler“ anstelle des Rückrufs „Erfolg“ aufruft. Bei Implementierungen, die Fortschrittsfunktionen enthalten, kann ein Versprechen seinen Fortschritt jederzeit aktualisieren, bevor es den unerfüllten Zustand verlässt. Wenn der Fortschritt aktualisiert wird, wird allen Fortschrittsrückrufen der Wert von progress übergeben und sie werden sofort aufgerufen. Fortschrittsrückrufe werden anders gehandhabt als Erfolgs- und Fehlerrückrufe. Wenn Sie einen Fortschrittsrückruf registrieren, nachdem eine Fortschrittsaktualisierung stattgefunden hat, wird der neue Fortschrittsrückruf erst für den aktualisierten Fortschritt aufgerufen, nachdem er registriert wurde.
Wir werden nicht weiter darauf eingehen, wie der Promise-Status verwaltet wird, da dies außerhalb der Spezifikation liegt und jede Implementierung unterschiedlich ist. In späteren Beispielen werden Sie sehen, wie es gemacht wird, aber das ist alles, was Sie jetzt wissen müssen.

Umgang mit Rückrufen

Wie bereits erwähnt, ist die Handhabung von Rückrufen für asynchrone Vorgänge die grundlegendste und häufigste Verwendung von Versprechen. Vergleichen wir einen Standardrückruf mit einem Rückruf, der Versprechen verwendet.

// Normal callback usage
asyncOperation(function() {
  // Here's your callback
});
// Now `asyncOperation` returns a promise
asyncOperation().then(function(){
  // Here's your callback
});

Ich bezweifle, dass es irgendjemandem wirklich Spaß machen würde, Versprechen zu verwenden, wenn er nur dieses Beispiel sieht. Es scheint keinen Vorteil zu geben, außer dass „then“ deutlicher macht, dass die Rückruffunktion aufgerufen wird, nachdem der asynchrone Vorgang abgeschlossen ist. Aber selbst mit diesem Vorteil haben wir jetzt mehr Code (durch die Abstraktion sollte unser Code kürzer werden, oder?) und Versprechen sind etwas weniger performant als Standard-Callbacks.

Aber lassen Sie sich davon nicht aufhalten. Wenn das das Beste wäre, was Versprechen bewirken könnten, gäbe es diesen Artikel nicht

Pyramide des Untergangs

// Normal callback usage => PYRAMID OF DOOOOOOOOM
asyncOperation(function(data){
  // Do some processing with `data`
  anotherAsync(function(data2){
    // Some more processing with `data2`
    yetAnotherAsync(function(){
      // Yay we're finished!
    });
  });
});
// Let's look at using promises
asyncOperation()
.then(function(data){
  // Do some processing with `data`
  return anotherAsync();
})
.then(function(data2){
  // Some more processing with `data2`
  return yetAnotherAsync();
})
.then(function(){
  // Yay we're finished!
});

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

异步操作返回一个promise对象。因此我们在那个promise对象中调用then,并且传给它一个回调函数;then也会返回一个promise。当异步操作结束,它将给promise装上数据。然后(第一次)回调被调用,数据被作为参数传递进去。如果回调不含有返回值,then返回的promise将会立即不带值组装。如果回调返回的不是一个promise,那么then返回的 promise将会立即装载那个数值。如果回调返回一个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 DOOOOOOOOM
asyncOperation(handler1);
function handler1(data) {
  // Do some processing with `data`
  anotherAsync(handler2);
}
function handler2(data2) {
  // Some more processing with `data2`
  yetAnotherAsync(handler3);
}
function handler3() {
  // Yay we're 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 we're 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 you've never seen before. It'll impress you
}

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

并行异步操作

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

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

使用promise的.when方法将会使这种“多个异步操作”例子变得普通,然而响应多个异步事件需要一个并不普通的代码量。
他完全正确。事实上我没有比较那两种情况。那篇文章的要点实际在于说明promises不是异步操作的唯一机制,而且在一些情况下,它们也不一定是最好的。在这个评论者指出的情况下,promises当然是最佳的解决办法。我们来看看他说的是什么

jQuery 具有 一个名为when的方法 ,可以带上任意数量的promise参数,并返回一个单一的promise。如果任何一个promise传入失败,when返回的promise也会失败。如果所有的promises被装载,那么每个值都将会按照promises被定义的顺序传递给附加的回调。

以并行的方式执行无数的异步操作非常有用,然后只要在它们之中的每一个结束之后继续执行回调。我们看一个简单的例子。

jQuery.when

// Each of these async functions return a promise
var 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
  }
);

Orang sering mengatakan ini adalah salah satu perkara terbaik tentang janji, dan ini adalah sebahagian daripada perkara yang menjadikan janji penting. Saya juga fikir ini adalah ciri yang baik yang memudahkan banyak operasi, tetapi mekanisme ini apabila kaedah tidak disebut dalam mana-mana spesifikasi Janji sama sekali, jadi saya tidak fikir ia adalah titik Janji. Terdapat spesifikasi yang menyebut kaedah bila, tetapi ia sama sekali berbeza daripada yang di atas. Setakat yang saya tahu, jQuery adalah satu-satunya perpustakaan yang melaksanakan kaedah ini apabila. Pustaka janji lain, seperti Q, Dojo, dan apabila melaksanakan kaedah bila mengikut spesifikasi Promises/B, tetapi jangan melaksanakan kaedah bila yang disebut oleh pengulas. Walau bagaimanapun, pustaka Q mempunyai kaedah semua, dan when.js juga mempunyai kaedah selari, yang berfungsi sama seperti kaedah jQuery.when di atas, kecuali ia menerima parameter jenis tatasusunan dan bukannya bilangan parameter sewenang-wenangnya.

Perwakilan nilai

Janji ialah cara yang lebih baik untuk mengendalikan senario berikut:

"Saya mahu mencari pengguna dalam pangkalan data ini, tetapi kaedah carian tidak segerak."

Jadi, di sini kita mempunyai kaedah cari yang tidak dapat mengembalikan nilai serta-merta. Tetapi akhirnya ia "mengembalikan" nilai (melalui panggilan balik), dan anda mahu mengendalikan nilai itu dalam beberapa cara. Kini, dengan menggunakan panggilan balik, anda boleh menentukan kesinambungan, atau "beberapa kod yang akan mengendalikan nilai itu pada masa akan datang"

Janji menukar "hei, ini beberapa kod yang anda akan dapati sendiri gunakan untuk mengendalikan nilai pulangan". Ia adalah kaedah yang membenarkan kaedah "cari" untuk mengatakan "Hei, saya akan sibuk mencari maklumat yang anda cari, tetapi sementara itu anda boleh terus menunggu keputusan dikembalikan, dan anda boleh memprosesnya dalam apa jua cara yang anda mahukan pada masa yang sama, seperti barangan Nyata ”

Janji mewakili nilai sebenar. Itulah perangkapnya. Ia berfungsi apabila anda mengendalikan Janji seperti perkara sebenar. Pelaksanaan JavaScript Promise mengharapkan anda memberikannya fungsi panggil balik Ini hanyalah "kebetulan", ia bukan perkara penting.

Saya percaya ini benar-benar titik janji. kenapa? Baca ayat pertama spesifikasi Promise/A, "Janji mewakili nilai yang dipulangkan dengan penyiapan operasi Menjadikannya agak jelas, bukan? Nah, walaupun itu maksudnya, itu tidak menghalang saya daripada membentangkan pandangan orang lain kemudian dalam artikel ini. Apa-apa pun, mari kita bercakap lebih sedikit tentang idea ini.

Kesimpulan

Inti janji ialah ia mewakili nilai hasil akhir yang dikembalikan oleh operasi, tetapi sebab untuk menggunakannya adalah untuk menjadikan operasi segerak selari dengan lebih baik. Sejak pengaturcaraan tak segerak memasuki tempat kejadian, panggilan balik telah muncul di mana-mana, mengaburi kod kami dengan cara yang pelik. Janji adalah cara untuk mengubahnya. Janji membolehkan kami menulis kod secara segerak sambil memberikan kami pelaksanaan tak segerak kod tersebut.

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn