Written in the beginning

  • ES6 common but ignored methods series of articles, sorting out the author thinks that some daily development may use some methods, use skills and some application scenarios, details please see related content links, welcome to supplement exchange.

Related articles

  • Common but overlooked methods for ES6 (Destruct assignments and values first)
  • ES6 Common but ignored methods (second bullet functions, arrays, and objects)
  • ES6 common but ignored methods (third bullet Symbol, Set, and Map)
  • ES6 commonly used but overlooked methods (fourth bullet Proxy and Reflect)
  • ES6 common but ignored methods (# 5 Promise and Iterator)
  • Common but ignored methods for ES6 (Generator 6)
  • ES6 Common but ignored methods (eighth bullet Class)
  • ES6 common but ignored methods (Module 9)
  • Common but ignored methods of ES6 (Development specification of the Tenth Bullet Project)
  • ES6 common but ignored method (eleventh bullet Decorator)
  • Common but overlooked approaches to ES6 (End game – Latest Proposal)

Async function

  • ES6 – async function

introduce

  • asyncThe function isGeneratorFunction syntax sugar.
const fs = require('fs'); const readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(fileName, function(error, data) { if (error) return reject(error); resolve(data); }); }); }; // Generator const gen = function* () {const f1 = yield readFile('/etc/fstab'); const f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; // async const asyncReadFile = async function () {const f1 = await readFile('/etc/fstab'); const f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };Copy the code
  • asyncFunction ofGeneratorFunction improvement:
    1. GeneratorFunction execution must depend on the executor, hence the existence ofcoModule (based onES6Generatoryield, allowing us to write asynchronous code in synchronous form), andasyncFunctions come with their own actuators.(asyncFunction execution, as normal functions, as long as one line.
    asyncReadFile();
    Copy the code
    • GeneratorFunction, need to be callednextMethod, or usecoModule, can really execute, get the final result.
    const gg = gen();
    gg.next() 
    Copy the code
    1. Better semantics.asyncandawaitCompared to*andyield, the semantics are clearer.asyncIt means that there are asynchronous operations in the function,awaitIndicates that the following expression needs to wait for the result.
    2. Wider applicability.coModule convention,yieldCommand can only be followed byThunkfunctionorPromiseObject, andasyncFunction of theawaitAfter the command, it can bePromiseObject and primitive type values (numeric, string, and Boolean, but this is automatically converted to immediateresolvedPromiseObject).
    3. The return value isPromise.asyncThe return value of the function isPromiseObject, this is more thanGeneratorThe return value of the function isIteratorObjects are much more convenient.

use

  1. The base is held
    • asyncThe function returns aPromiseObject that can be usedthenMethod to add a callback function.asyncFunction of the internalreturnThe value returned by the statement becomesthenMethod to call the argument to the function. asyncThe function throws an error internally, which will result in a returnPromiseObject torejectState. The thrown error object will be removedcatchThe method callback function receives.
    async function getName() { return 'detanx'; } getName().then(val => console.log(val)) // "detanx" async function f() {throw new Error(' Error '); } f().then(v => console.log(v), e => console.log(e)) // Error: Error occurredCopy the code
  2. use
    // async function foo() {} const foo = async function () {}; Let obj = {async foo() {}}; obj.foo().then(...) // Arrow function const foo = async () => {}; // Class method Class Storage {constructor() {this.cachePromise = caches. Open ('avatars'); } async getAvatar(name) { const cache = await this.cachePromise; return cache.match(`/avatars/${name}.jpg`); } } const storage = new Storage(); Storage. GetAvatar (' jake). Then (...). ;Copy the code
  3. Change of state
    • asyncFunction returnedPromiseObject,Must wait inside allawaitAfter commandPromiseA state change does not occur until the object is executed, unless encounteredreturnStatement or throw an error.
  4. awaitThe command
    • Under normal circumstances,awaitThe command is followed by onePromiseObject that returns the result of that object. If it is notPromiseObject directly returns the corresponding value.
    async function getName() {
      return 'detanx';
    }
    getName().then(val => console.log(val))
    // "detanx"
    Copy the code
    • On the other hand,awaitThe command is followed by onethenableObject (definedthenMethod object), thenawaitIt’s going to be equal toPromiseObject.
    class Sleep { constructor(timeout) { this.timeout = timeout; } then(resolve, reject) { const startTime = Date.now(); setTimeout( () => resolve(Date.now() - startTime), this.timeout ); } } (async () => { const sleepTime = await new Sleep(1000); console.log(sleepTime); }) (); / / 1000Copy the code
    • Any oneawaitAfter the statementPromiseObject torejectState, so the wholeasyncThe function breaks execution.
      Async function f() {await promise.reject (' error '); await Promise.resolve('hello world'); // do not execute}Copy the code
      • If the previous asynchronous operation failed, do not interrupt subsequent asynchronous operations.
      1. awaitOn thetry... catchinside
      Async function f() {try {await promise.reject (' error '); } catch(e) { } return await Promise.resolve('detanx'); } f() .then(v => console.log(v)) // detanxCopy the code
      1. awaitAt the back of thePromiseObject followed by onecatchMethod to handle errors that may have occurred earlier.
      Async function f() {await promise.reject (' error '). Catch (e => console.log(e)); async function f() {await promise.reject (' error '). return await Promise.resolve('detanx'); } f().then(v => console.log(v)Copy the code
  5. Use caution points
    1. awaitAfter commandPromiseObject, the result of which might berejectedSo it’s better toawaitCommand intry... catchCode block.
    2. multipleawaitAsynchronous operations following a command, if there is no secondary relationship, should be fired at the same time.
    // let [foo, bar] = await Promise. All ([getFoo(), getBar()]); // let fooPromise = getFoo(); let barPromise = getBar(); let foo = await fooPromise; let bar = await barPromise;Copy the code
    1. awaitCommands can only be used whenasyncFunction, if used in ordinary functions, will report an error. The correct way to write it is to useforLooping or using arraysreduceMethods. If you want multiple requests to be executed concurrently, you can usePromise.allorPromise.allSettledMethods.
    async function dbFuc(db) { let docs = [{}, {}, {}]; Async function docs.forEach(function (doc) {await db.post(doc); }); => for loop for (let doc of docs) {await db.post(doc); } => array reduce method await docs. Reduce (async (_, doc) => {await _; await db.post(doc); }, undefined); }Copy the code
    1. asyncThe function can preserve the run stack.
    const a = () => {
      b().then(() => c());
    };
    Copy the code
    • In the above code, function A runs an asynchronous task b() inside. When b() runs, function A () does not interrupt, but continues execution. By the time b() finishes running, it is possible that A () has long since finished running and the context in which B () exists has disappeared. If b() or c() reports an error, the error stack will not include a().

    • Now change this example to an async function.

    const a = async () => {
      await b();
      c();
    };
    Copy the code
    • In the code above,b()When it runs,a()It’s a pause, the context is saved. Once theb()orc()Error, the error stack will containa().

implementation

  • asyncAnd the way that functions are implemented is that theGeneratorFunction and autoexecutor, wrapped in one function.
async function fn(args) { // ... Function fn(args) {return spawn(function* () {//... }); }Copy the code
  • All of theasyncFunctions can be written in the second form above, wherespawnThe function is the autoexecutor. The followingspawnFunction implementation, is basically the previous version of the autoexecutor.
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}
Copy the code

Comparison of asynchronous processing methods

  • Traditional methods, prior to ES6, there were roughly four methods of asynchronous programming.

    1. The callback function
    2. Event listeners
    3. Publish/subscribe
    4. Promiseobject
  • Through an example, mainly look at the Promise, Generator function and async function comparison, others are not familiar with you can look up the relevant information.

  • Suppose a series of animations are deployed on a DOM element, and the first animation ends before the next one begins. If one of the animations fails, it does not proceed further and returns the return value of the last successfully executed animation.

    1. PromiseThe writing of
    Function chainAnimationsPromise(elem, animations) {// The variable ret is used to keep the return value of the previous animation let ret = null; // create an empty Promise let p = promise.resolve (); For (let anim of animations) {p = p. Chen (function(val) {ret = val; return anim(elem); }); Function (e) {/* ignore the error, continue */}). Then (function() {return ret; }); }Copy the code
    • At first glance, the code is allPromiseAPI(then,catchAnd so on), the semantics of the operation itself are not easy to see.
    1. GeneratorThe way you write a function.
    function chainAnimationsGenerator(elem, animations) { return spawn(function*() { let ret = null; try { for(let anim of animations) { ret = yield anim(elem); }} catch(e) {return ret; }); }Copy the code
    • Semantic thanPromiseWritten more clearly, the user – defined actions all appear inspawnThe inside of the function. The problem is that you have to have a task runner that does it automaticallyGeneratorFunction of the code abovespawnThe function is the autoexecutor, and it returns onePromiseObject, and must be guaranteedyieldThe expression following the statement must return onePromise.
    1. asyncThe way you write a function.
    async function chainAnimationsAsync(elem, animations) { let ret = null; try { for(let anim of animations) { ret = await anim(elem); }} catch(e) {return ret; }Copy the code
    • AsyncThe function implementation is the most concise and semantically compliant, with almost no semantically irrelevant code.
  • All asynchronous processing methods exist as reasonable, there is no best, only the most appropriate, in dealing with different practical situations, we can choose the most suitable processing method.

The instance

  • In real life development, you often encounter a set of asynchronous operations that need to be done sequentially. For example, reading a group remotely in turnURL, and output the results in the order they were read.
    1. PromiseIn the writing.
    Function logInOrder(urls) {const textPromises = urls.map(URL => {return fetch(URL). Then (response =>) response.text()); }); TextPromises. Reduce ((chain, textPromise) => { return chain.then(() => textPromise) .then(text => console.log(text)); }, Promise.resolve()); }Copy the code
    • The above code usesfetchMethod while reading a group remotelyURL. eachfetchAll operations return onePromiseObject, put intextPromisesThe array. And then,reduceMethod handles each in turnPromiseObject, and then usethenAnd all thePromiseObjects are linked together so that results can be output in sequence.
    1. asyncFunction implementation.
    Async function logInOrder(urls) {// Async function logInOrder(urls) {const textPromises = urls.map(async URL => {const response = await fetch(url); return response.text(); }); // Output for (const textPromise of textPromises) {console.log(await textPromise); }}Copy the code
    • In the code above, althoughmapThe argument to the method isasyncFunction, but it is executed concurrently because onlyasyncThe internal execution of a function is secondary, and the external execution is unaffected. At the back of thefor.. ofThe loop is used internallyawait, so the sequential output is realized.

topawait

  • nowawaitCommands can only appear inasyncFunction inside, otherwise an error will be reported. There is aGrammar proposal(The bill is currently inStatus: Stage 3), allowing independent use at the top level of the moduleawaitCommand so that the above line of code does not report an error.The purpose of this proposal is to borrowawaitSolve the problem of asynchronous module loading.
    1. The moduleawaiting.jsThe output value ofoutputDepending on the asynchronous operation.
    // awaiting.js let output; (async function1 main() { const dynamic = await import(someMission); const data = await fetch(url); output = someProcess(dynamic.default, data); }) (); export { output };Copy the code
    1. loadingawaiting.jsThe module
    // usage.js
    import { output } from "./awaiting.js";
    
    function outputPlusValue(value) { return output + value }
    console.log(outputPlusValue(100));
    setTimeout(() => console.log(outputPlusValue(100), 1000);
    Copy the code
    • outputPlusValue()The result of the execution depends entirely on the execution time. ifawaiting.jsThe asynchronous operation in it is not finished. It is loaded inoutputThe value isundefined.
  • The current solution is to have the original module output onePromiseObject, from thisPromiseThe object determines whether an asynchronous operation has ended.
    // usage.js
    import promise, { output } from "./awaiting.js";
    
    function outputPlusValue(value) { return output + value }
    promise.then(() => {
      console.log(outputPlusValue(100));
      setTimeout(() => console.log(outputPlusValue(100), 1000);
    });
    Copy the code
    • In the code above, willawaiting.jsThe output of the object is placed inpromise.then()In order to ensure that the asynchronous operation is complete before readingoutput.
    • This is cumbersome, requiring the user of the module to follow an extra protocol to use the module in a particular way. Once you forget to use itPromiseLoad, using only normal load methods, code that relies on this module may fail. And if the topusage.jsAnd then there’s the external output, which is equal to all the modules in the dependency chainPromiseLoad.
  • The top-level await command guarantees that the module will output the value only after the asynchronous operation has completed.
    // awaiting.js
    const dynamic = import(someMission);
    const data = fetch(url);
    export const output = someProcess((await dynamic).default, await data);
    
    // usage.js
    import { output } from "./awaiting.js";
    function outputPlusValue(value) { return output + value }
    
    console.log(outputPlusValue(100));
    setTimeout(() => console.log(outputPlusValue(100), 1000);
    Copy the code
    • In the above code, both asynchronous operations are added to the outputawaitCommand. The module will not output values until the asynchronous operation is complete.
  • At the topawaitSome usage scenarios for.
    // the import() method loads const strings = await import(' /i18n/${navigator.language} '); // Database operation const connection = await dbConnector(); // rely on rollback let jQuery; try { jQuery = await import('https://cdn-a.com/jQuery'); } catch { jQuery = await import('https://cdn-b.com/jQuery'); }Copy the code
  • If more than one contains the top layerawaitThe load command is executed synchronously.
    // x.js
    console.log("X1");
    await new Promise(r => setTimeout(r, 1000));
    console.log("X2");
    
    // y.js
    console.log("Y");
    
    // z.js
    import "./x.js";
    import "./y.js";
    console.log("Z");
    Copy the code
    • The code above has three modules, the last onez.jsloadingx.jsandy.js, the printed result isX1,Y,X2,Z. This shows,z.jsThere was no waitingx.jsLoad, load againy.js.
    • The top of theawaitCommands are a bit like handing over execution of code to other modules to load, then taking it back after the asynchronous operation is complete and continuing.