Heim >Web-Frontend >js-Tutorial >Sprechen Sie über die Entwicklungsgeschichte asynchroner JavaScript-Funktionen_Javascript-Fähigkeiten

Sprechen Sie über die Entwicklungsgeschichte asynchroner JavaScript-Funktionen_Javascript-Fähigkeiten

WBOY
WBOYOriginal
2016-05-16 15:37:301469Durchsuche

Die Fremdsprache „The Evolution of Asynchronous JavaScript“ hat die Entwicklungsgeschichte asynchroner JavaScript-Funktionen geklärt. Zuerst wurden asynchrone Funktionen durch Callback-Funktionen realisiert und dann durch Promise/A- und Generatorfunktionen asynchrone Funktionen sein. Vielen Dank an Jingzhuang für die Übersetzung dieses Artikels. Der Inhalt lautet wie folgt:

Lassen Sie uns nun einen Blick auf die Entwicklung asynchroner JavaScript-Funktionen im Laufe der Jahre werfen.

Rückruffunktion Rückrufe

Es scheint, dass alles mit der Rückruffunktion beginnen sollte.

Asynchrones JavaScript

Wie wir wissen, kann die asynchrone Programmierung in JavaScript nur über erstklassige Bürgerfunktionen in der JavaScript-Sprache erfolgen: Auf diese Weise können wir eine Funktion als Parameter einer anderen Funktion übergeben innerhalb der Funktion (d. h. der Callback-Funktion) aufgerufen werden. Aus diesem Grund wurde die Rückruffunktion geboren: Wenn Sie eine Funktion als Parameter an eine andere Funktion übergeben (zu diesem Zeitpunkt wird sie als Funktion höherer Ordnung bezeichnet), können Sie diese Funktion innerhalb der Funktion aufrufen, um die entsprechende Aufgabe abzuschließen. Die Callback-Funktion hat keinen Rückgabewert (versuchen Sie nicht, return zu verwenden) und wird nur zum Ausführen bestimmter Aktionen innerhalb der Funktion verwendet. Schauen Sie sich ein Beispiel an:

Something.save(function(err) {  
 if (err) { 
  //error handling 
  return; // 没有返回值 
 } 
 console.log('success'); 
}); 

Im obigen Beispiel haben wir eine Fehler-zuerst-Rückruffunktion (Fehler-zuerst-Rückrufe) demonstriert, die auch eine der Eigenschaften von Node.js selbst ist. Alle Kernmodule in Node.js und die meisten Module im NPM-Repository Diese Funktion wird beim Schreiben befolgt.

Herausforderungen bei übermäßiger Nutzung von Rückruffunktionen:

Wenn Sie den Code nicht vernünftig organisieren können, kann es sehr leicht zu einer Rückrufhölle kommen, wodurch Ihr Code für andere schwer verständlich wird.
Es ist leicht, Fehlerbehandlungscode zu übersehen.

Die Return-Anweisung kann nicht zur Rückgabe eines Werts verwendet werden, und das Schlüsselwort throw kann nicht verwendet werden.

Aus diesen Gründen sind wir in der JavaScript-Welt immer auf der Suche nach praktikablen Lösungen, die die asynchrone JavaScript-Entwicklung einfacher machen können.

Eine der möglichen Lösungen ist das Async-Modul. Wenn Sie sich schon lange mit Rückruffunktionen beschäftigen, haben Sie möglicherweise ein tiefes Verständnis dafür, wie komplex es ist, wenn Sie in JavaScript etwas parallel oder seriell ausführen lassen oder sogar eine asynchrone Funktion verwenden möchten, um etwas in einem Array abzubilden ist das Element, das asynchrone Funktionen verwendet. Vielen Dank also an Caolan McMahon für das Schreiben des Async-Moduls zur Lösung dieser Probleme.

Mit dem Async-Modul können Sie ganz einfach Code auf diese Weise schreiben:

async.map([1, 2, 3], AsyncSquaringLibrary.square,  
 function(err, result){ 
 // result will be [1, 4, 9] 
}); 

Obwohl das Async-Modul bis zu einem gewissen Grad Komfort bietet, ist es immer noch nicht einfach genug und der Code ist nicht leicht zu lesen, daher erschien Promise.

Versprechen

Der aktuelle asynchrone JavaScript-Standard stammt aus dem Jahr 2012 und wurde erst mit ES6 verfügbar. Der Begriff Promise wurde jedoch nicht von der JavaScript-Community erfunden. Der Begriff stammt aus einem Artikel von Daniel P.friedman aus dem Jahr 1976.

Ein Promise stellt das Endergebnis einer asynchronen Operation dar.

Jetzt verwenden wir Promise, um die durch den obigen Code erledigten Aufgaben abzuschließen. Der Promise-Stilcode lautet wie folgt:

Something.save()  
 .then(function() { 
  console.log('success'); 
 }) 
 .catch(function() { 
  //error handling 
 }) 

Sie werden feststellen, dass Rückruffunktionen auch in Promise verwendet werden. Sowohl in der then- als auch in der Catch-Methode wird eine Rückruffunktion übergeben, die ausgeführt wird, wenn das Versprechen erfüllt bzw. abgelehnt wird. Ein weiterer Vorteil von Promise-Funktionen besteht darin, dass sie verkettet werden können, um eine Reihe von Aufgaben zu erledigen. Sie können beispielsweise Code wie diesen schreiben:

saveSomething()  
 .then(updateOtherthing) 
 .then(deleteStuff)  
 .then(logResults); 

Wenn Sie kein fertiges Promise haben, müssen Sie möglicherweise auf einige Promise-Bibliotheken zurückgreifen. Eine beliebte Wahl ist die Verwendung von Bluebird. Diese Bibliotheken bieten möglicherweise mehr Funktionalität als native Lösungen und sind nicht auf die im Promise/A-Standard festgelegten Funktionen beschränkt.

Aber warum greift man nicht auf Zuckermethoden zurück? Es wird empfohlen, dass Sie zuerst den Artikel Promise: The Extension Problem lesen. Weitere Informationen zu Promise finden Sie im Promise/A-Standard.

Sie fragen sich vielleicht: Wenn die meisten Bibliotheken nur Rückrufschnittstellen bereitstellen, wie sollte ich dann Promise verwenden?

Nun, das ist ganz einfach. Das Einzige, was Sie zu diesem Zeitpunkt tun müssen, ist, Promise zu verwenden, um den Funktionsaufrufkörper mit dem Rückruf zu umschließen. Zum Beispiel:

Der Code für den Rückrufstil könnte so aussehen:

function saveToTheDb(value) { 
  db.values.insert(value, function (err, user) { 
    if (err) throw err; 
 
    // todo: insert user to db 
  }); 
} 

Jetzt ändern wir es in Code, der Aufrufe im Promise-Stil unterstützt:

function saveToTheDb(value) {  
  return new Promise(function(resolve, reject) { 
    db.values.insert(value, function(err, user) { // remember error first ;) 
      if (err) { 
        return reject(err); // don't forget to return here 
      } 
      resolve(user); 
    }) 
  } 
} 

Eine beträchtliche Anzahl von Bibliotheken oder Frameworks unterstützen bereits beide Methoden, das heißt, sie bieten API-Schnittstellen sowohl im Callback-Stil als auch im Promise-Stil. Wenn Sie nun also auch der Außenwelt eine Bibliothek zur Verfügung stellen möchten, ist es die beste Vorgehensweise, beide Schnittstellen gleichzeitig bereitzustellen. Sie können dieses Ziel ganz einfach mit den folgenden Methoden erreichen:

function foo(cb) {  
 if (cb) { 
  return cb(); 
 } 
 return new Promise(function (resolve, reject) { 
 
 }); 
} 

或者更简单些,你可以从只提供Promise风格的接口开始后,并使用诸如 callbackify这样的工具来达到向后兼容的目的。其实Callbackify所做的工作和上面的代码片段类似,但在实现上使用了一个更通用的方法, 我建议你可以去阅读Callbackify的源代码。

生成器Generators/ yield

JavaScript 生成器是个相对较新的概念, 它是ES6(也被称为ES2015)的新特性。想象下面这样的一个场景:

当你在执行一个函数的时候,你可以在某个点暂停函数的执行,并且做一些其他工作,然后再返回这个函数继续执行, 甚至是携带一些新的值,然后继续执行。
上面描述的场景正是JavaScript生成器函数所致力于解决的问题。当我们调用一个生成器函数的时候,它并不会立即执行, 而是需要我们手动的去执行迭代操作(next方法)。也就是说,你调用生成器函数,它会返回给你一个迭代器。迭代器会遍历每个中断点。

function* foo () {  
 var index = 0; 
 while (index < 2) { 
  yield index++; //暂停函数执行,并执行yield后的操作 
 } 
} 
var bar = foo(); // 返回的其实是一个迭代器 
console.log(bar.next());  // { value: 0, done: false }  
console.log(bar.next());  // { value: 1, done: false }  
console.log(bar.next());  // { value: undefined, done: true } 

 更进一步的,如果你想更轻松的使用生成器函数来编写异步JavaScript代码,我们可以使用 co 这个库,co是著名的tj大神写的。

Co是一个为Node.js和浏览器打造的基于生成器的流程控制工具,借助于Promise,你可以使用更加优雅的方式编写非阻塞代码。
使用co,前面的示例代码,我们可以使用下面的代码来改写:

co(function* (){  
 yield Something.save(); 
}).then(function() { 
 // success 
}) 
.catch(function(err) { 
 //error handling 
}); 

你可能会问:如何实现并行操作呢?答案可能比你想象的简单,如下(其实它就是Promise.all而已):

yield [Something.save(), Otherthing.save()];  
Async/ await

在ES7(还未正式标准化)中引入了Async函数的概念,目前如果你想要使用的话,只能借助于babel 这样的语法转换器将其转为ES5代码。(提醒一点:我们现在讨论的是async关键字,而不是NPM中的async包)。

简而言之,使用async关键字,你可以轻松地达成之前使用生成器和co函数所做到的工作。当然,除了hack之外。

也许你会问,是否在ES7中有了async关键字,yield就变得不是那么重要了?

实际上,使用yield实现异步也不过是一种hack罢了,yield意味着懒次序(lazy sequences)和迭代器。 而await能够完美的分离这两点,首先让yield用于其最初的目的,其次使用await来执行异步操作。

在这背后,async函数实际使用的是Promise,也就是为什么async函数会返回一个Promise的原因。

因此,我们使用async函数来完成类似于前面代码所完成的工作,可以使用下面这样的方式来重新编写代码:

async function save(Something) {  
 try { 
  await Something.save(); // 等待await后面的代码执行完,类似于yield 
 } catch (ex) { 
  //error handling 
 } 
 console.log('success'); 
} 

正如你看到的那样,使用async函数,你需要在函数声明的最前面加上async关键字。这之后,你可以在函数内部使用await关键字了,作用和之前的yield作用是类似的。

使用async函数完成并行任务与yiled的方式非常的相似,唯一不同的是,此时Promise.all不再是隐式的,你需要显示的调用它:

async function save(Something) {  
  await Promise.all[Something.save(), Otherthing.save()] 
} 

Koa也支持async函数,如果你也在使用koa,那么你现在就可以借助babel使用这一特性了。

import koa from koa;  
let app = koa(); 
app.experimental = true; 
app.use(async function (){  
  this.body = await Promise.resolve('Hello Reader!') 
}) 
app.listen(3000);  

以上内容给大家分享了JavaScript异步函数发展历程,希望对大家有所帮助。

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