Maison  >  Article  >  interface Web  >  Parlez de l'historique du développement des fonctions asynchrones JavaScript_compétences Javascript

Parlez de l'historique du développement des fonctions asynchrones JavaScript_compétences Javascript

WBOY
WBOYoriginal
2016-05-16 15:37:301422parcourir

Le langage étranger "L'évolution du JavaScript asynchrone" a trié l'historique du développement des fonctions asynchrones JavaScript. Tout d'abord, les fonctions asynchrones ont été réalisées via des fonctions de rappel, puis elles sont passées par les fonctions Promise/A et génératrices. être des fonctions asynchrones. Merci à Jingzhuang pour la traduction de cet article, le contenu est le suivant :

Passons maintenant en revue le développement des fonctions asynchrones JavaScript au fil des ans.

Fonction de rappel Rappels

Il semble que tout devrait commencer par la fonction de rappel.

JavaScript asynchrone

Comme nous le savons, en JavaScript, la programmation asynchrone ne peut se faire qu'à travers des fonctions citoyennes de premier ordre dans le langage JavaScript : cela signifie que nous pouvons passer une fonction en tant que paramètre d'une autre fonction, dans cette fonction transmise peut être appelé à l'intérieur de la fonction (c'est-à-dire la fonction de rappel). C'est pourquoi la fonction de rappel est née : si vous transmettez une fonction en paramètre à une autre fonction (on l'appelle actuellement fonction d'ordre supérieur), alors à l'intérieur de la fonction, vous pouvez appeler cette fonction pour terminer la tâche correspondante. La fonction de rappel n'a pas de valeur de retour (ne soyez pas tenté d'utiliser return) et n'est utilisée que pour effectuer certaines actions au sein de la fonction. Jetez un œil à un exemple :

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

Dans l'exemple ci-dessus, nous avons démontré une fonction de rappel d'erreur en premier (rappels en premier), qui est également l'une des caractéristiques de Node.js lui-même. Tous les modules de base de Node.js et la plupart des modules du référentiel NPM. Cette fonctionnalité sera suivie lors de l’écriture.

Défis liés à l'utilisation excessive des fonctions de rappel :

Si vous ne pouvez pas organiser le code de manière raisonnable, il est très facile de provoquer un enfer de rappel (enfer de rappel), ce qui rendra votre code difficile à comprendre par les autres.
Il est facile de manquer le code de gestion des erreurs.

L'instruction return ne peut pas être utilisée pour renvoyer une valeur et le mot-clé throw ne peut pas être utilisé.

C'est pour ces raisons que dans le monde JavaScript, nous sommes toujours à la recherche de solutions réalisables pouvant faciliter le développement JavaScript asynchrone.

L'une des solutions possibles est le module async. Si vous utilisez des fonctions de rappel depuis longtemps, vous comprenez peut-être profondément qu'en JavaScript, si vous souhaitez exécuter quelque chose en parallèle ou en série, ou même utiliser une fonction asynchrone pour mapper quelque chose dans un tableau, quelle est la complexité est l'élément utilisant des fonctions asynchrones. Merci donc à Caolan McMahon d'avoir écrit le module async pour résoudre ces problèmes.

En utilisant le module async, vous pouvez facilement écrire du code de cette manière :

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

Bien que le module async apporte une certaine commodité, il n'est toujours pas assez simple et le code n'est pas facile à lire, c'est pourquoi Promise est apparue.

Promesse

Le standard asynchrone JavaScript actuel remonte à 2012 et n'est devenu disponible qu'ES6. Cependant, le terme Promise n'a pas été inventé par la communauté JavaScript. Le terme vient d'un article publié par Daniel P.friedman en 1976.

Une promesse représente le résultat final d'une opération asynchrone.

Nous utilisons maintenant Promise pour effectuer les tâches effectuées par le code ci-dessus. Le code de style Promise est le suivant :

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

Vous constaterez que les fonctions de rappel sont également utilisées dans Promise. Une fonction de rappel est transmise dans les méthodes then et catch, qui sont respectivement exécutées lorsque la promesse est satisfaite et rejetée. Un autre avantage des fonctions Promise est qu’elles peuvent être enchaînées pour accomplir une série de tâches. Par exemple, vous pouvez écrire du code comme ceci :

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

Lorsque vous n'avez pas de Promise prête à l'emploi, vous devrez peut-être recourir à certaines bibliothèques Promise. Un choix populaire consiste à utiliser bluebird. Ces bibliothèques peuvent fournir plus de fonctionnalités que les solutions natives et ne se limitent pas aux fonctionnalités spécifiées par la norme Promise/A.

Mais pourquoi n’utilisez-vous pas des méthodes sucrées ? Il est recommandé de lire d’abord l’article Promesse : le problème de l’extension. Pour plus d’informations sur Promise, veuillez vous référer à la norme Promise/A.

Vous vous demandez peut-être : si la plupart des bibliothèques n'exposent que des interfaces de rappel, comment dois-je utiliser Promise ?

Eh bien, c'est très simple. La seule chose que vous devez faire pour le moment est d'utiliser Promise pour envelopper le corps de l'appel de fonction contenant le rappel. Par exemple :

Le code de style de rappel pourrait ressembler à ceci :

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

Maintenant, nous le modifions en code qui prend en charge les appels de style Promise :

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); 
    }) 
  } 
} 

Un nombre considérable de bibliothèques ou de frameworks prennent déjà en charge les deux méthodes, c'est-à-dire qu'ils fournissent à la fois des interfaces API de style rappel et de style Promise. Alors maintenant, si vous souhaitez également fournir une bibliothèque au monde extérieur, la meilleure pratique consiste à fournir les deux interfaces en même temps. Vous pouvez facilement utiliser les méthodes suivantes pour atteindre cet objectif :

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异步函数发展历程,希望对大家有所帮助。

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn