Heim >Web-Frontend >js-Tutorial >Detaillierte Erläuterung der Verwendung von Async in Javascript

Detaillierte Erläuterung der Verwendung von Async in Javascript

巴扎黑
巴扎黑Original
2017-08-23 14:00:153916Durchsuche

In diesem Artikel wird hauptsächlich die Verwendung von Javascript Async vorgestellt. Der Herausgeber findet es ziemlich gut. Jetzt werde ich es mit Ihnen teilen und es als Referenz geben. Folgen wir dem Editor und werfen wir einen Blick darauf.

Vorab geschrieben

In diesem Artikel wird eine optimale Methode zum sequentiellen Lesen von Dateien implementiert. Die Implementierungsmethode beginnt mit Die älteste Rückrufmethode für die aktuelle Async-Methode. Ich werde Ihnen auch mein Verständnis der Thunk-Bibliothek und der Co-Bibliothek mitteilen. Der erzielte Effekt: Lesen Sie a.txt und b.txt nacheinander und verketten Sie den gelesenen Inhalt zu einer Zeichenfolge.

Synchronisiertes Lesen


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

Diese Methode ist für unser Verständnis am förderlichsten, und der Code ist sehr gut Klar, nein Zu viel Verschachtelung, gute Wartung, aber das hat das größte Problem, nämlich die Leistung. Was der Knoten befürwortet, ist asynchrone E/A, um intensive E/A zu bewältigen, und synchrones Lesen ist größtenteils eine Verschwendung Die Nachteile dieser Methode überwiegen offensichtlich die Vorteile, also lassen Sie sie einfach bestehen. (Tatsächlich besteht das Ziel jeder asynchronen Programmierlösung im Knoten darin, synchrone Semantik und asynchrone Ausführung zu erreichen.)

Verwenden Sie Rückrufe zum Lesen


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

Mit der Rückrufmethode ist es sehr einfach, sie direkt zu verschachteln. In diesem Fall kann es jedoch leicht zu einer Situation kommen, die schwer zu warten und schwer zu verstehen ist. Die Extremsituation ist die Callback-Hölle.

Promise-Implementierung


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

Promise kann horizontale Wachstumsrückrufe in vertikales Wachstum umwandeln, was einige Probleme lösen kann Das Problem, aber das durch Promise verursachte Problem ist die Code-Redundanz. Auf den ersten Blick ist das alles nicht sehr komfortabel, aber im Vergleich zur Verschachtelung von Rückruffunktionen wurde es erheblich verbessert.

Yield

Generator ist in vielen Sprachen verfügbar. Es handelt sich im Wesentlichen um eine Coroutine :

  • Prozess: Die Grundeinheit der Ressourcenzuweisung im Betriebssystem

  • Thread: Die Grundeinheit der Ressourcenplanung im Betriebssystem

  • Coroutine: eine Ausführungseinheit, die kleiner als ein Thread ist, mit eigenem CPU-Kontext, einer Coroutine und einem Stack

Es können mehrere Threads vorhanden sein In einem Prozess können mehrere Coroutinen vorhanden sein. Der Wechsel von Prozessen und Threads wird vom Betriebssystem gesteuert, während der Wechsel von Coroutinen vom Programmierer selbst gesteuert wird. Asynchrone E/A verwendet Rückrufe, um intensive E/A-Vorgänge zu bewältigen. Das Wechseln von Coroutinen verschwendet nicht viele Ressourcen. Schreiben Sie einen E/A-Vorgang in eine Coroutine und gehen Sie so vor. O, Sie können die CPU an andere Coroutinen abgeben.

js unterstützt auch Coroutinen, was Yield ist. Das intuitive Gefühl, das uns die Verwendung von yield vermittelt, ist, dass die Ausführung an dieser Stelle stoppt und anderer Code weiter ausgeführt wird. Wenn Sie möchten, dass die Ausführung fortgesetzt wird, wird er auch weiterhin ausgeführt.


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

Sequentielles Lesen unter yield stellt auch eine sequentielle Lesemethode dar. Es gibt zwei verschiedene Implementierungsmethoden für readFile:

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

Die Thunkify-Funktion ist eine Art Curry-Idee. Der letzte eingehende Parameter ist die Callback-Funktion. Der automatisierte Prozess der Yield-Funktion kann einfach implementiert werden :


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

Use Promise


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

Beide der beiden oben genannten Methoden können erreicht werden Gibt es eine Möglichkeit, die mit diesen beiden Implementierungsmethoden kompatibel ist, nämlich die Co-Bibliothek?


Schauen wir uns die Implementierung der Co-Bibliothek an. Die Co-Bibliothek gibt standardmäßig ein Promise-Objekt zurück (z. B. res.value oben). Die Co-Bibliothek wird es in ein Versprechen umwandeln. Die Implementierungsidee ist sehr einfach und verwendet im Wesentlichen die Rekursion:
// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify
// co库返回一个Promise对象
co(readTwoFile).then(data => console.log(data));


toPromise besteht darin, einige Typen in Promise umzuwandeln Das Wichtigste ist, welche Typen hinter yield platziert werden können. Hier ist ein häufig verwendeter Typ:
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'));
    }
  });
}


Seit kurzem unterstützt Node async/await, was möglich ist Wird verwendet, um asynchrone Operationen auszuführen:
// 把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);
    });
  });
}

Ultimative Lösung



Was async/await bewirkt, ist die Verkettung von Promise Objekte Es vermeidet die dann aufrufende Methode und der Code ist sehr gut lesbar. Es ist eine synchrone Methode. Sie müssen sich nicht mehr auf andere externe Klassenbibliotheken (z. B. Co-Bibliotheken) verlassen, um das Rückrufproblem elegant zu lösen
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);
});

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der Verwendung von Async in Javascript. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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