Emulation implements async/await

This is the 11th day of my participation in Gwen Challenge

Introduction to the

Async, await, await, await, await, await, await, await, await, await

In development, the only direct access to Generator writing is currently with DVA

This article with the purpose of learning, and you share the specific implementation details

async/await

  • async
  • await

With async and await, it is very easy to use promises to change asynchronous behavior to synchronous

async function fn(){
  await promise1()
  await promise2()
  await promise3()
}
fn()
Copy the code

Is syntactic sugar for Generator+Promise

The async function returns a Promis by default

Generator

Also called a generator, returns an iterable object

  • Generator
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
const obj = gen()
for(const o of obj){
  console.log(o) / / 1 2 3
}
Copy the code

Symbol.iterator

Iterable protocol. Objects can be iterated only if they implement the symbol. iterator method

  • Symbol.iterator
const obj = {
  0:'000'.1:'777'.2:'666'.length:3[Symbol.iterator]:[][Symbol.iterator]
}
for(const o of obj){
  console.log(o) / / 000 777 666
}
Copy the code

Define the object as an Array of classes, and then use Array’s symbol. iterator directly

Analog implementation

The first stage

  1. Async returns a Promise
  2. Automatic execution generator
const testGen1 = function* () {
  const a = yield Promise.resolve(1);
  console.time('temp')
  const b = yield new Promise((res) = > {
    setTimeout(() = > {
      res(2);
    }, 2000);
  });
  const c = yield Promise.resolve(3);
  console.log(a, b, c);
  console.timeEnd('temp')};function myAsync1(generator) {
  return new Promise(
    (resolve, reject) = > {
      // Get the iterator object
      const gen = generator();

      function _next(doneValue) {
        const {
          done,
          value,
        } = doneValue;

        if (done) {
          resolve();
          return;
        }

        // Execute the next one after executing this one
        value.then(() = >{ _next(gen.next()); }); } _next(gen.next()); }); } myAsync1(testGen1)// Output the result
undefined undefined undefined
temp: 2.007s
Copy the code

The second stage

  1. The variable to the left of await normally accepts the value
  2. The right side of the await non-promise is automatically wrapped as a promise
  3. Can handle async return values correctly
const testGen2 = function* () {
  const a = yield 1;
  console.time('temp')
  const b = yield new Promise((res) = > {
    setTimeout(() = > {
      res(2);
    }, 2000);
  });
  const c = yield 3;
  console.log(a, b, c);
  console.timeEnd('temp')
  return [a, b, c];
};
function myAsync2(generator) {
  return new Promise(
    (resolve, reject) = > {
      // Get the iterator object
      const gen = generator();

      function _next(doneValue) {
        const {
          done,
          value,
        } = doneValue;

        if (done) {
          // Handle async return values correctly
          resolve(value);
          return;
        }

        // Execute the next one after executing this one
        // Non-promise is automatically wrapped as promise
        Promise.resolve(value).then(
          (data) = > {
            // Assign the contents of Promise resolve to the variable to the left of await_next(gen.next(data)); }); } _next(gen.next()); }); } myAsync2(testGen2).then(console.log);
// Run the result
1 2 3
temp: 2.005s
[ 1.2.3 ]
Copy the code

The third stage

  1. A reject error that correctly throws a Promise
const testGen3 = function* () {
  console.time('temp');
  const b = yield new Promise(
    (res, rej) = > {
      setTimeout(() = > {
        res(2);
      }, 2000); });console.log(b);
  try {
    const d = yield Promise.reject(4);
  } catch (error) {
    console.log(error);
  }
  console.timeEnd('temp');
};

function myAsync3(generator) {
  return new Promise(
    (resolve, reject) = > {
      // Get the iterator object
      const gen = generator();

      function _next(doneValue) {
        const {
          done,
          value,
        } = doneValue;

        if (done) {
          // Handle async return values correctly
          resolve(value);
          return;
        }

        // Execute the next one after executing this one
        // Non-promise is automatically wrapped as promise
        Promise.resolve(value)
          .then((data) = > {
            // Assign the contents of Promise resolve to the variable to the left of await
            _next(gen.next(data));
          })
          .catch((err) = > {
            // Catching an exception throws an error to the generator
            // And resume the generator's execution, returning an object with done and value attributes._next(gen.throw(err)); }); } _next(gen.next()); }); } myAsync3(testGen3);// Run the result
2
4
temp: 2.009s
Copy the code

The fourth stage

  1. Async can catch errors thrown at runtime by non-await expressions
const testGen4 = function* () {
  console.time('temp');
  const b = yield new Promise(
    (res, rej) = > {
      setTimeout(() = > {
        res(2);
      }, 2000); });console.log(b);
  console.timeEnd('temp');
  b = 'err';
};

function myAsync4(generator) {
  return new Promise(
    (resolve, reject) = > {
      // Get the iterator object
      const gen = generator();

      function _next(doneValue) {
        const {
          done,
          value,
        } = doneValue;

        if (done) {
          // Handle async return values correctly
          resolve(value);
          return;
        }

        // Execute the next one after executing this one
        // Non-promise is automatically wrapped as promise
        Promise.resolve(value)
          .then((data) = > {
            // Catch errors thrown by non-yield expressions inside the generator
            try {
              // Assign the contents of Promise resolve to the variable to the left of await
              _next(gen.next(data));
            } catch (err) {
              reject(err);
            }
          })
          .catch((err) = > {
            // Catching an exception throws an error to the generator
            // And resume the generator's execution, returning an object with done and value attributes.
            _next(gen.throw(err));
          });
      }

      // Catch errors thrown by non-yield expressions inside the generator
      try {
        _next(gen.next());
      } catch(err) { reject(err); }}); } myAsync4(testGen4).catch((err) = > {
  console.log('catch err');
  console.log(err);
});
// Run the result
2
temp: 2.008s
catch err
TypeError: Assignment to constant variable.
Copy the code

In the end

Make some optimizations for the redundant myAsync code described above

function myAsync(generator) {
  return new Promise(
    (resolve, reject) = > {
      // Get the iterator object
      const gen = generator();

      function _next(doneValue) {
        const { done, value } =
          doneValue || {};

        if (done) {
          // Handle async return values correctly
          resolve(value);
          return;
        }

        // Execute the next one after executing this one
        // Non-promise is automatically wrapped as promise
        Promise.resolve(value)
          .then((data) = > {
            // Catch errors thrown by non-yield expressions inside the generator
            try {
              // Assign the contents of Promise resolve to the variable to the left of await
              _next(gen.next(data));
            } catch (err) {
              reject(err);
            }
          })
          .catch((err) = > {
            // Catching an exception throws an error to the generator
            // And resume the generator's execution, returning an object with done and value attributes._next(gen.throw(err)); }); } _next(); }); }Copy the code

test

Case 1

async/await

async function demo1() {
  const a = await 1;
  const b = await new Promise(
    (res, rej) = > {
      setTimeout(() = > {
        res(2);
      }, 2000); });const c = await Promise.resolve(3);
  console.log(a, b, c);

  try {
    const d = await Promise.reject(4);
  } catch (error) {
    console.log(error);
  }
  return [a, b, c];
}
demo1().then(console.log).catch(err= >{
    console.log('catch err');
    console.log(err);
})

// Output the result
1 2 3
4
[ 1.2.3 ]
Copy the code

MyAsync implementation test

myAsync(function* () {
  const a = yield 1;
  const b = yield new Promise(
    (res, rej) = > {
      setTimeout(() = > {
        res(2);
      }, 2000); });const c = yield Promise.resolve(3);
  console.log(a, b, c);

  try {
    const d = yield Promise.reject(4);
  } catch (error) {
    console.log(error);
  }

  return [a, b, c];
})
  .then(console.log)
  .catch((err) = > {
    console.log('catch err');
    console.log(err);
  });
// Output the result
1 2 3
4
[ 1.2.3 ]
Copy the code

Case 2

await/async

async function demo2() {
  const a = await 'hello';
  const b = await 'world';
  console.log(a, b);

  b = 'err';

  return a + b;
}
demo2()
  .then(console.log)
  .catch((err) = > {
    console.log('catch err');
    console.log(err);
  });
// Output the result
hello world
catch err
TypeError: Assignment to constant variable.
Copy the code

MyAsync test

myAsync(function* () {
  const a = yield 'hello';
  const b = yield 'world';
  console.log(a, b);

  b = 'err';

  return a + b;
})
  .then(console.log)
  .catch((err) = > {
    console.log('catch err');
    console.log(err);
  });
// Output the result
hello world
catch err
TypeError: Assignment to constant variable.
Copy the code