Помогает вам начать понимать async/await

внешний интерфейс Vue.js React.js Promise

Изучая фронтенд, я кое-что обобщаю сам в виде блогов.Конечно, я также надеюсь помочь некоторым друзьям, которые начали изучать фронтенд, как и я.

Если есть ошибка, укажите на нее в комментариях, я исправлюсь

автор: Томасжоу

Давайте начнем изучать асинхронность и ожидание

Async / await использует синхронное мышление для решения асинхронных проблем.

  • async的优点

    • 利用async创建的函数也是异步函数,就像setTimeout那种一样
    • async/await 的优势在于处理 then 链:
      • Если вам нужно иметь дело с цепочкой then, состоящей из нескольких промисов, преимущество может быть отражено, потому что передача параметров промиса слишком хлопотна, а async/await особенно удобен.
      • Async может напрямую получать переданные переменные, но тогда от peomise не зависит.Если вы хотите получить значение, вы должны выставить часть данных в самом внешнем слое и затем присвоить его один раз внутри.
  • 相比较generator

    • (1)Встроенный привод. Выполнение функции Generator должно полагаться на исполнителя, поэтому существует библиотека функций co, а у асинхронной функции есть собственный исполнитель. То есть,Выполнение асинхронной функции точно такое же, как и у обычной функции, только одна строка.
    • (2)лучшая семантика. async и await имеют более четкую семантику, чем звездочки и yield. async означает, что в функции есть асинхронная операция, а await означает, что следующему выражению необходимо дождаться результата.
    • (3)более широкая применимость. Согласно соглашению библиотеки функций, за командой yield может следовать только функция Thunk или объект Promise, иЗа командой await асинхронной функции могут следовать объекты Promise и значения примитивных типов (числа, строки и логические значения, но это эквивалентно синхронной операции)).

Синхронное и асинхронное понимание: когда мы делаем запрос, мы не будем ждать результата ответа, а продолжим выполнение кода позади, а обработка результата ответа будет разрешена в последующем цикле событий. Тогда смысл синхронизации в том, что код будет продолжать выполняться после того, как выйдет результат.

Мы можем использовать сценарий вопросов и ответов для двух человек в качестве аналогии между асинхронным и синхронным. После того, как A задает вопрос B, он не ждет ответа B, а затем задает следующий вопрос, который является асинхронным. После того, как А задает вопрос Б, он с улыбкой ждет и ждет ответа Б. После ответа Б он задает следующий вопрос. Это синхронизация.

1、安装支持

babel уже поддерживается, поэтому мы можем использовать его в webpack Сначала загрузите babel-loader с помощью npm в текущем проекте.

npm install babel-loader --save-dev

Затем в файле конфигурации件webpack.confing.dev.js中配置,在module.exports.module.rules中Просто добавьте следующие элементы конфигурации.

  {
    test: /\.(js|jsx)$/,
    include: paths.appSrc,
    loader: require.resolve('babel-loader'),
    options: {
      cacheDirectory: true,
    },
  },

Если вы используете последнюю версию create-react-app или vue-cli для сборки своего кода, они уже должны поддерживать эту конфигурацию.

2、普通声明和await使用

  • Асинхронная функция фактически возвращает объект Promise.
async function fn() {
    return 30;
}

// 或者
const fn = async () => {
    return 30;
}

При объявлении функции добавьте ключевое слово async впереди, что означает использование async. Когда мы используем console.log для вывода объявленной выше функции fn, мы можем увидеть следующие результаты:

console.log(fn());

//result
Promise = {
    __proto__: Promise,
    [[PromiseStatus]]: "resolved",
    [[PromiseValue]]: 30
}

Очевидно, что результатом запуска fn является объект Promise. Таким образом, мы также можем использовать then для обработки последующей логики.

fn().then(res => {
    console.log(res);  // 30
})

Использование ожидания --------------------

ожидание означает ожидание.То есть код должен дождаться запуска функции ожидания и возврата результата, прежде чем продолжить выполнение следующего кода. Это как раз и есть эффект синхронизации.

Но мы должны обратить внимание, что,Ключевое слово await можно использовать только в асинхронных функциях. И функция ожидания должна возвращать объект Promise для достижения эффекта синхронизации..

Когда мы используем переменную для получения возвращаемого значения ожидания,如:const temp = await fn();Возвращаемое значение temp — это значение, разрешенное в Promise (то есть PromiseValue)..

// 定义一个返回Promise对象的函数
function fn() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(30);
        }, 1000);
    })
}

// 然后利用async/await来完成代码
const foo = async () => {
    const t = await fn(); // 将30传入
    console.log(t);
}

foo();
console.log('begin')

// begin
// 30

Во-первых, мы определяем функцию fn(), эта функция возвращает обещание и задерживает 1 секунду, разрешает и передает значение 30, функция foo использует ключевое слово async, когда оно определено, а затем использует ожидание в теле функции, и, наконец, выполнить foo(). Вся программа будет выводить 30 через 1 секунду, то есть константа t в foo() получает значение разрешения в fn() и блокирует выполнение следующего кода через await до тех пор, пока не сработает асинхронная функция fn(). выполняется.

Запустив этот пример, мы видим, что когда асинхронная функция сталкивается с ожиданием, она будет ждать завершения работы функции, стоящей за ожиданием, и не будет выполнять следующий код напрямую.

Вы можете видеть, что begin отдает приоритет выходным данным, потому что функция foo(), созданная с помощью async/await, также является асинхронной функцией, так что вы понимаете

Если мы напрямую используем метод обещания then, для достижения того же результата мы должны написать последующую логику в методе then.

const foo = () => {
    return fn().then(t => {
        console.log(t);
        console.log('next code');    
    })
}
foo();

Очевидно, что при использовании async/await структура кода будет лаконичнее, а логика — понятнее.

Нетрудно увидеть из фрагмента кода, что Promise не решает хороших вещей, таких как наличие большого количества then-методов, весь блок кода будет заполнен Promise-методами, а не самой бизнес-логикой.

И каждый метод then является независимой областью действия.Если вы хотите обмениваться данными, вы должны предоставить некоторые данные самому внешнему слою и затем присвоить значение внутри него.

Тем не менее, Promise очень хорош для инкапсуляции асинхронных операций, поэтому async/await основан на Promise, а за await следует получение экземпляра Promise.

3、异常处理

В Promise мы знаем, что исключения перехватываются с помощью catch.А когда мы используем асинхронность, мы используем try/catch для перехвата исключений.

function fn() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('some error.');
        }, 1000);
    })
}

const foo = async () => {
    try {
        await fn();
    } catch (e) {
        console.log(e);  // some error
    }
}

foo();

Объект Promise после команды await может быть отклонен, поэтому текущий результат может быть отклонен.Команду await лучше поместить в блок try...catch.

Если есть несколько функций ожидания, возвращайте толькопервое пойманное исключение.

function fn1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('some error fn1.');// 设置reject
        }, 1000);
    })
}
function fn2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('some error fn2.'); // 设置reject
        }, 1000);
    })
}

const foo = async () => {
    try {
        await fn1();
        await fn2();
    } catch (e) {
        console.log(e);  // some error fn1.
    }
}

foo();
async

4、async/await 的优势在于处理 then 链

Одна цепочка промисов не может найти преимущества async/await, но если вам нужно иметь дело с цепочкой then, состоящей из нескольких промисов, преимущества могут быть отражены (интересно, что Promise решает проблему многоуровневых обратных вызовов через then). chain , а теперь используйте async/await для дальнейшей оптимизации).

Пример первый:

Предположим, бизнес завершается в несколько шагов, каждый шаг асинхронен и зависит от результата предыдущего шага. Мы по-прежнему используем setTimeout для имитации асинхронных операций:

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}
  • Обещание способа добиться обработки этих трех шагов
function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => step2(time2))
        .then(time3 => step3(time3))
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

  • Асинхронный/ожидающий способ реализовать обработку этих трех шагов
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time2);
    const result = await step3(time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

Результат такой же, как и в предыдущей реализации Promise, но этот код выглядит намного чище, почти так же, как синхронный код

Пример второй:

Теперь измените бизнес-требования, это по-прежнему три шага, но каждый шаг требует результатов каждого предыдущего шага.

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(m, n) {
    console.log(`step2 with ${m} and ${n}`);
    return takeLongTime(m + n);
}

function step3(k, m, n) {
    console.log(`step3 with ${k}, ${m} and ${n}`);
    return takeLongTime(k + m + n);
}
  • Используйте async/await, чтобы написать:
async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}

doIt();

За исключением того, что время выполнения больше, он вроде бы ничем не отличается от предыдущего примера! Не волнуйтесь, подумайте о том, как бы это выглядело, если бы это было написано как Обещание.

function doIt() {
    console.time("doIt");
    const time1 = 300;
    step1(time1)
        .then(time2 => {
            return step2(time1, time2)
                .then(time3 => [time1, time2, time3]);
        })
        .then(times => {
            const [time1, time2, time3] = times;
            return step3(time1, time2, time3);
        })
        .then(result => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        });
}

doIt();

Это кажется немного сложным? Эта куча обработки параметров — ахиллесова пята схемы Promise — передача параметров слишком хлопотна, и у меня кружится голова!

5、await in for 循环

  • Команду await можно использовать только в асинхронных функциях, при использовании в обычных функциях будет выдано сообщение об ошибке. (Обратите внимание, что forEach, map, reduce также являются функциями!!!!)
    • Правильный способ написать это использовать цикл for. !!!!
async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 报错
  docs.forEach(function (doc) {
    await db.post(doc);
  });
}

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

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  // 可能得到错误结果
  docs.forEach(async function (doc) {
    await db.post(doc);
  });
}

Приведенный выше код может работать неправильно, поскольку три операции db.post будут выполняться одновременно, то есть одновременно, а не последовательно.Правильный способ написать это использовать цикл for.

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  for (let doc of docs) {
    await db.post(doc);
  }
}
  • если ты действительно хочешьНесколько запросов выполняются одновременно, вы можете использовать метод Promise.all.
    • Сначала сохраните несколько функций (задач) в массив документов, вы можете сохранить несколько задач, а затем реализовать параллельное выполнение
async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的写法

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  // 将多个函数(任务)都保存到doc这个数组中,就可以保存多个任务,然后再实现并发执行
  let promises = docs.map((doc) => db.post(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}

6、实践

На практике большинство асинхронных сценариев, с которыми мы сталкиваемся, — это запросы к интерфейсу, поэтому здесь мы берем $.get в jquery в качестве примера, чтобы кратко показать, как решить этот сценарий с помощью async/await.

// 先定义接口请求的方法,由于jquery封装的几个请求方法都是返回Promise实例,因此可以直接使用await函数实现同步
const getUserInfo = () => $.get('xxxx/api/xx');

const clickHandler = async () => {
    try {
        const resp = await getUserInfo();
        // resp为接口返回内容,接下来利用它来处理对应的逻辑
        console.log(resp);

        // do something
    } catch (e) {
        // 处理错误逻辑
    }
}

7、一个问题测试

тема

Следующая функция aa() может быть изменена для вывода желаемого значения с помощью console.log() через одну секунду.

function aa() {
    setTimeout(function() {
        return "want-value";
    }, 1000);
}
  • Однако есть дополнительные требования:
    • Функцию aa() можно изменить по желанию, но она не может иметь console.log().
    • Операторы выполнения console.log() не могут иметь пакет setTimeout

отвечать

Основной целью задачи является исследование обработки результата выполнения асинхронного вызова, поскольку это асинхронный вызов, синхронно дождаться асинхронного результата невозможно, а результат должен быть асинхронным.

setTimeout() часто используется для имитации асинхронных операций. Самое раннее асинхронное — уведомлять (вызывать) обработчик результата обработки через обратный вызов.

function aa() {
    return new Promise((resolve) => {
      setTimeout(function() {
        resolve('want-value');
			},1000);
		});
  }

async function fn() {
    let temp = await aa();
    console.log(temp);
}
fn();

Справочная статья: