Maison >interface Web >js tutoriel >Explication détaillée de l'utilisation de async en javascript

Explication détaillée de l'utilisation de async en javascript

巴扎黑
巴扎黑original
2017-08-23 14:00:153916parcourir

Cet article présente principalement l'utilisation de la compréhension de javascript async. L'éditeur pense que c'est assez bon. Maintenant, je vais le partager avec vous et le donner comme référence. Suivons l'éditeur et jetons un coup d'œil.

Écrit devant

Cet article implémentera une méthode optimale pour lire séquentiellement les fichiers. La méthode d'implémentation commence à partir de. la plus ancienne méthode de rappel vers l'asynchrone actuelle, je partagerai également avec vous ma compréhension de la bibliothèque thunk et de la bibliothèque co. L'effet obtenu : lire a.txt et b.txt séquentiellement et concaténer le contenu lu en une chaîne.

Lecture synchrone


const readTwoFile = () => {
  const f1 = fs.readFileSync('./a.txt'),
    f2 = fs.readFileSync('./b.txt');
  return Buffer.concat([f1, f2]).toString();
};

Cette méthode est la plus propice à notre compréhension, et le code est très clair, non Trop d'imbrication, une bonne maintenance, mais cela pose le plus gros problème, à savoir les performances. Ce que le nœud préconise, ce sont les E/S asynchrones pour gérer les E/S intensives et la lecture synchrone, dans une large mesure, c'est un gaspillage. le processeur du serveur. Les inconvénients de cette méthode l'emportent évidemment sur les avantages, alors passez-la simplement. (En fait, l'objectif de toute solution de programmation asynchrone dans Node est d'obtenir une sémantique synchrone et une exécution asynchrone.)

Utilisez des rappels pour lire


const readTwoFile = () => {
  let str = null;
  fs.readFile('./a.txt', (err, data) => {
    if (err) throw new Error(err);
    str = data;
    fs.readFile('./b.txt', (err, data) => {
      if (err) throw new Error(err);
      str = Buffer.concat([str, data]).toString();
    });
  });
};

En utilisant la méthode de rappel, c'est très simple à mettre en œuvre. Il suffit de l'imbriquer directement. Cependant, dans ce cas, cela peut facilement conduire à des situations difficiles à maintenir et à comprendre. une situation extrême est un enfer de rappel.

Mise en œuvre de la promesse


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const readTwoFile = () => {
  let bf = null;
  readFile('./a.txt')
    .then(
      data => {
        bf = data;
        return readFile('./b.txt');
      }, 
      err => { throw new Error(err) }
    )
    .then(
      data => {
        console.log(Buffer.concat([bf, data]).toString())
      }, 
      err => { throw new Error(err) }
    );
};

La promesse peut convertir les rappels de croissance horizontale en croissance verticale, ce qui peut résoudre certains problèmes Problème, mais le problème causé par Promise est la redondance du code. En un coup d'œil, tout cela n'est pas très confortable, mais par rapport à l'imbrication des fonctions de rappel, cela a été grandement amélioré.

yield

Le générateur est disponible dans de nombreuses langues. Il s'agit essentiellement d'une coroutine. Examinons les différences et les connexions entre les coroutines, les threads et les processus. . :

  • Processus : l'unité de base d'allocation des ressources dans le système d'exploitation

  • Thread : l'unité de base de planification des ressources dans le système d'exploitation.

  • Coroutine : une unité d'exécution plus petite qu'un thread, avec son propre contexte CPU, une coroutine et une pile

Il peut y avoir plusieurs threads dans un processus. Il peut y avoir plusieurs coroutines dans un thread. La commutation des processus et des threads est contrôlée par le système d'exploitation, tandis que la commutation des coroutines est contrôlée par le programmeur lui-même. Les E/S asynchrones utilisent des rappels pour gérer les E/S intensives. Vous pouvez également utiliser des coroutines pour les gérer. Le changement de coroutine ne gaspille pas beaucoup de ressources. Écrivez une opération d'E/S dans une coroutine et procédez comme ceci pendant les E/S. O, vous pouvez abandonner le CPU à d'autres coroutines.

js prend également en charge les coroutines, qui correspondent au rendement. Le sentiment intuitif que nous donne l'utilisation de rendement est que l'exécution s'arrête à cet endroit et que d'autres codes continuent de s'exécuter. Lorsque vous souhaitez qu'il continue son exécution, il continuera à s'exécuter.


function *readTwoFile() {
  const f1 = yield readFile('./a.txt');
  const f2 = yield readFile('./b.txt'); 
  return Buffer.concat([f1, f2]).toString();
}

La lecture séquentielle sous rendement présente également une méthode de lecture séquentielle. Il existe deux méthodes d'implémentation différentes pour readFile,

Utilisez thunkify


const thunkify = (fn, ctx) => (...items) => (done) => {
  ctx = ctx || null;
  let called = false;
  items.push((...args) => {
    if (called) return void 0;
    called = true;
    done.apply(ctx, args);
  });
  try {
    fn.apply(ctx, items);  
  } catch(err) {
    done(err);
  }
};

La fonction thunkify est une sorte d'idée de curry. Le dernier paramètre entrant effectué est la fonction de rappel. Utilisation de thunkify Le processus automatisé de la fonction rendement peut être facilement implémenté. :


const run = fn => {
  const gen = fn();
  let res;
  (function next(err, data) {
    let g = gen.next(data);
    if (g.done) return void 0;
    g.value(next);
  })();
};

Utiliser la promesse


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const run = fn => {
  const gen = fn();
  let str = null;
  (function next(err, data) {
    let res = gen.next(data);
    if (res.done) return void 0;
    res.value.then(
      data => {
        next(null, data);
      }, 
      err => { throw new Error(err); }
    );
  })();
};
run(readTwoFile);

Les deux méthodes ci-dessus peuvent atteindre le processus d'exécution automatique de rendement, existe-t-il donc un moyen compatible avec ces deux méthodes d'implémentation ? Master tj a donné une autre bibliothèque, qui est la bibliothèque co. Jetons d'abord un coup d'œil à l'utilisation :

.

// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify
// co库返回一个Promise对象
co(readTwoFile).then(data => console.log(data));

Jetons un coup d'œil à l'implémentation de la bibliothèque co. La bibliothèque co renverra un objet Promise par défaut pour la valeur après rendement (comme res.value ci-dessus), The. co library le convertira en promesse. L'idée d'implémentation est très simple, utilisant essentiellement la récursion. L'idée générale est la suivante :


const baseHandle = handle => res => {
  let ret;
  try {
    ret = gen[handle](res);
  } catch(e) {
    reject(e);
  }
  next(ret);
};
function co(gen) {
  const ctx = this,
    args = Array.prototype.slice.call(arguments, 1);
  return new Promise((reslove, reject) => {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    const onFulfilled = baseHandle('next'),
      onRejected = baseHandle('throw');

    onFulfilled();

    function next(ret) {
      if (ret.done) reslove(ret.value);
      // 将yield的返回值转换为Proimse
      const value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('yield type error'));
    }
  });
}

toPromise consiste à convertir certains types en Promise. La chose la plus importante est de savoir quels types peuvent être placés derrière le rendement. En voici un couramment utilisé :


// 把thunkify之后的函数转化为Promise的形式
function thunkToPromise(fn) {
  const ctx = this;
  return new Promise(function (resolve, reject) {
    fn.call(ctx, function (err, res) {
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments, 1);
      resolve(res);
    });
  });
}

Récemment, Node a pris en charge async/await, qui peut être. utilisé pour effectuer des opérations asynchrones :

Solution ultime


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const readTwoFile = async function() {
  const f1 = await readFile('./a.txt');
  const f2 = await readFile('./b.txt');  
  return Buffer.concat([f1, f2]).toString();
};
readTwoFile().then(data => {
  console.log(data);
});

Ce que fait async/await, c'est concaténer la promesse objets Cela évite la méthode appelante et le code est très lisible. C'est une méthode synchrone. Vous n'avez plus besoin de compter sur d'autres bibliothèques de classes externes (telles que les co-bibliothèques) pour résoudre avec élégance le problème de rappel

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!

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