Элегантность async/await никогда не выходит из моды

внешний интерфейс JavaScript

введение

async/awaitЭто отличный синтаксический сахар, и можно сказать, что он является окончательным решением проблемы асинхронности. Воспринимайте это буквально. асинхронный异步означает, что пока ожидание等待, поэтому поймите, что async используется для объявления функции асинхронной, а await используется для ожидания завершения асинхронного метода.

src=http___pic.962.net_up_2019-12_15767448543514326.png&refer=http___pic.962.jpeg

асинхронная функция

asyncобъявляет функцию как асинхронную функцию, которая возвращаетpromiseобъект, вы можете использовать метод then для добавления функции обратного вызова.asyncвнутри функцииreturnЗначение, возвращаемое оператором, станетthenПараметры функции обратного вызова метода.

async function test() {
  return 'test';
}
console.log(test); // [AsyncFunction: test] async函数是[`AsyncFunction`]构造函数的实例
console.log(test()); // Promise { 'test' }

// async返回的是一个promise对象
test().then(res=>{
  console.log(res); // test
})

// 如果async函数没有返回值 async函数返回一个undefined的promise对象
async function fn() {
  console.log('没有返回');
}
console.log(fn()); // Promise { undefined }

// 可以看到async函数返回值和Promise.resolve()一样,将返回值包装成promise对象,如果没有返回值就返回undefined的promise对象

await

Оператор await можно использовать только внутри асинхронной функции. Если Promise передается оператору await, await будет ждать, пока Promise завершится в обычном режиме и вернет результат своей обработки, что означает, что он заблокирует код позади, ожидая результата объекта Promise. Если ожидаемый объект не является объектом Promise, возвращается само значение.

async function test() {
  return new Promise((resolve)=>{
    setTimeout(() => {
        resolve('test 1000');
    }, 1000);
  })
}
function fn() {
  return 'fn';
}

async function next() {
    let res0 = await fn(),
        res1 = await test(),
        res2 = await fn();
    console.log(res0);
    console.log(res1);
    console.log(res2);
}
next(); // 1s 后才打印出结果 为什么呢 就是因为 res1在等待promise的结果 阻塞了后面代码。

обработка ошибок

еслиawaitПосле ошибки это эквивалентноasyncОбъект Promise, возвращаемый функцией,reject.

async function test() {
  await Promise.reject('错误了')
};

test().then(res=>{
  console.log('success',res);
},err=>{
  console.log('err ',err);
})
// err 错误了

Способ предотвращения ошибок также состоит в том, чтобы помещать их вtry...catchв кодовом блоке.

async function test() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('错误了');
    });
  } catch(e) {
      console.log('err', e)
  }
  return await('成功了');
}

несколькоawaitАсинхронные операции, следующие за командой, если нет вторичной связи (то есть независимых друг от друга), лучше всего позволить им запускаться в одно и то же время.

let foo = await getFoo();
let bar = await getBar();
// 上面这样写法 getFoo完成以后,才会执行getBar

// 同时触发写法 ↓

// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()]);

// 写法二
let fooPromise = getFoo();
let barPromise = getBar();
let foo = await fooPromise;
let bar = await barPromise;

Преимущества асинхронного/ожидания

Преимущество async/await заключается в том, чтобы иметь дело с цепочкой then, состоящей из нескольких промисов.В предыдущей статье про промисы упоминалась проблема использования then для борьбы с callback hell.Async/await эквивалентен дальнейшей оптимизации промисов. Предположим, бизнес разделен на несколько шагов, и каждый шаг асинхронен и зависит от результата выполнения предыдущего шага.

// 假设表单提交前要通过俩个校验接口

async function check(ms) { // 模仿异步 
  return new Promise((resolve)=>{
    setTimeout(() => {
        resolve(`check ${ms}`);
    }, ms);
  })
}
function check1() {
  console.log('check1');
  return check(1000);
}
function check2() {
  console.log('check2');
  return check(2000);
}

// -------------promise------------
function submit() {
  console.log('submit');
  // 经过俩个校验 多级关联 promise传值嵌套较深
  check1().then(res1=>{
    check2(res1).then(res2=>{
       /*
        * 提交请求
        */
    })
  })
}
submit();

// -------------async/await-----------
async function asyncAwaitSubmit() {
    let res1 = await check1(),
        res2 = await check2(res1);
        console.log(res1, res2);
        /*
        * 提交请求
        */
}


принцип

Принцип реализации асинхронной функции заключается в том, чтобы обернуть функцию-генератор и автоматический исполнитель в функцию.

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () {
    // ...
  });
}
/*
* Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。
* 异步操作需要暂停的地方,都用 yield 语句注明
* 调用 Generator 函数,返回的是指针对象(这是它和普通函数的不同之处),。调用指针对象的 next 方法,会移动内部指针。
* next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。
*/

// 了解generator的用法
function* Generator() {
  yield '1';
  yield  Promise.resolve(2);
  return 'ending';
}
 
var gen = Generator(); // 返回指针对象 Object [Generator] {}

let res1 = gen.next();
console.log(res1); // 返回当前阶段的值 { value: '1', done: false }

let res2 = gen.next();
console.log(res2); // 返回当前阶段的值 { value: Promise { 2 }, done: false }

res2.value.then(res=>{
  console.log(res); // 2
})

let res3 = gen.next();
console.log(res3);  // { value: 'ending', done: true }

let res4 = gen.next();
console.log(res4); // { value: undefined, done: true }

Генератор реализует асинхронную функцию

// 接受一个Generator函数作为参数
function spawn(genF) {
  // 返回一个函数
  return function() {
     // 生成指针对象
     const gen = genF.apply(this, arguments);
     // 返回一个promise
     return new Promise((resolve, reject) => {
          // key有next和throw两种取值,分别对应了gen的next和throw方法
          // arg参数则是用来把promise resolve出来的值交给下一个yield
          function step(key, arg) {
            let result;

                // 监控到错误 就把promise给reject掉 外部通过.catch可以获取到错误
                try {
                  result = gen[key](arg)
                } catch (error) {
                  return reject(error)
                }

                // gen.next() 返回 { value, done } 的结构
                const { value, done } = result;

                if (done) {
                      // 如果已经完成了 就直接resolve这个promise
                      return resolve(value)
                } else {
                      // 除了最后结束的时候外,每次调用gen.next()
                      return Promise.resolve(
                        // 这个value对应的是yield后面的promise
                        value
                      ).then((val)=>step("next", val),(err) =>step("throw", err))
                }
          }
          step("next")
     })
  }
}

тестовое задание

function fn(nums) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(nums)
    }, 1000)
  })
}
// async 函数
async function testAsync() {
  let res1 = await fn(1);
  console.log(res1); // 1
  let res2 = await fn(2);
  console.log(res2); // 2
  return res2;
}
let _res = testAsync();
console.log('testAsync-res',_res); // Promise
_res.then(v=>console.log('testAsync-res',v)) // 2

// Generator函数
function* gen() {
  let res1 = yield fn(3);
  console.log(res1); // 3
  let res2 = yield fn(4);
  console.log(res2); // 4
  // let res3 = yield Promise.reject(5);
  //  console.log(res3);
  return res2;
}

let _res2 = spawn(gen)();
console.log('gen-res',_res2); // Promise

_res2
.then(v=>console.log('gen-res',v)) // 4
.catch(err=>{console.log(err)}) // res3 执行会抛出异常


Суммировать

Синтаксический сахар async/await может сделать асинхронный код чище и читабельнее, так что сворачивайте его быстро. Заинтересованные генераторы могут узнать об этом.