Если есть ошибка, укажите на нее в комментариях, я исправлюсь
автор: Томасжоу
Давайте начнем изучать асинхронность и ожидание
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();
Справочная статья: