Maison > interface Web > js tutoriel > le corps du texte

Introduction détaillée à l'historique du développement des fonctions asynchrones JavaScript et exemples de code

黄舟
Libérer: 2017-03-11 15:12:14
original
1673 Les gens l'ont consulté

Pour la plupart des développeurs JavaScript, la fonction asynchrone est une nouveauté et son développement a parcouru un long chemin. Par conséquent, cet article tente de trier et de résumer l'historique de développement des fonctions JavaScript asynchrones : il n'y a pas si longtemps, nous ne pouvions écrire que des fonctions de rappel pour obtenir l'asynchronisme, puis le standard Promise/A est sorti, puis les fonctions génératrices sont apparues. L’avenir est évidemment aux fonctions asynchrones.

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

Fonction de rappel Rappels

Il semble que tout devrait partir de 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 : cette méthode signifie que nous peut utiliser une fonction comme paramètre d'une autre fonction, et la fonction passée (c'est-à-dire la fonction de rappel) peut être appelée à l'intérieur de cette fonction. C'est pourquoi la fonction de rappel est née : si vous passez 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. Regardez un exemple :

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

Dans l'exemple ci-dessus, nous démontrons 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. Les modules Node.js Core et la plupart des modules du référentiel NPM sont écrits pour suivre cette fonctionnalité.

Défis rencontrés par une 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 (callback hell), qui vous faire Le code est difficile à comprendre pour 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 qui peuvent faciliter le développement JavaScript asynchrone.

Une des solutions possibles est le module async. Si vous travaillez avec 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 (mapping) dans un tableau. Comment complexe 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 la manière suivante :

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

Bien que le module async apporte une certaine commodité, il n'est toujours pas assez simple et le code n'est pas facile à lire, d'où Promesse.

Promesse

La norme asynchrone JavaScript actuelle remonte à 2012 et n'est devenue disponible qu'à partir de l'ES6. Cependant, le terme Promesse 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  
  })
Copier après la connexion

Vous constaterez que les fonctions de rappel sont également utilisées dans. Promesse. 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 celui-ci :

saveSomething()    
  .then(updateOtherthing)  
  .then(deleteStuff)    
  .then(logResults);
Copier après la connexion

Lorsque vous n'avez pas de Promise prête à l'emploi, vous devrez peut-être utiliser 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 peut ressembler à ceci :

function saveToTheDb(value) {  
    db.values.insert(value, function (err, user) {  
        if (err) throw err;  

        // todo: insert user to db  
    });  
}
Copier après la connexion

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);  
        })  
    }  
}
Copier après la connexion

Vous avez déjà assez quelques bibliothèques ou frameworks prennent en charge les deux méthodes, c'est-à-dire qu'ils fournissent 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) {  

  });  
}
Copier après la connexion

或者更简单些,你可以从只提供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 }
Copier après la connexion

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

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

使用co,前面的示例代码,我们可以使用下面的代码来改写:

co(function* (){    
  yield Something.save();  
}).then(function() {  
  // success  
})  
.catch(function(err) {  
  //error handling  
});
Copier après la connexion

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

yield [Something.save(), Otherthing.save()];
Copier après la connexion

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(&#39;success&#39;);  
}
Copier après la connexion

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

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

async function save(Something) {    
    await Promise.all[Something.save(), Otherthing.save()]  
}
Copier après la connexion

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

import koa from koa;    
let app = koa();  

app.experimental = true;  

app.use(async function (){    
    this.body = await Promise.resolve(&#39;Hello Reader!&#39;)  
})  

app.listen(3000);
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal