В прошлый раз мы говорили о Promise, а сегодня поговорим о группе API async/await.
async/await — это стандарт ES7, Promise — стандарт ES6, async/await API также используется, чтобы помочь нам писать асинхронный код, он построен на Promise, немного похоже на отношения между Okhttp и Retrofit.
что такое асинхронность?
async function myFirstAsyncFunction() {
try {
const fulfilledValue = await doSomeThing();
}
catch (rejectedValue) {
// …
}
}
Наверное выглядит так. Асинхронность обычно не используется отдельно, а используется вместе с ожиданием, внутри асинхронной функции может быть ноль или более ожиданий.
Этот код легко понять, не изучая промисы. Вот где асинхронные функции великолепны.
Когда вызывается асинхронная функция, она немедленно возвращает Promise.
await
await нельзя использовать отдельно, оно выдаст ошибку, если будет вызвано внутри неасинхронной функции. За await обычно следует обещание или что-то еще, например значение, переменная или функция. Если за await не следует промис, он вернет разрешенный промис.
Когда асинхронная функция выполняется для ожидания, она приостанавливает процесс выполнения всей асинхронной функции и передает свой контроль, и возобновляет процесс только тогда, когда асинхронная операция на основе промисов, которую она ожидает, выполняется или отклоняется.
Конечно, асинхронная функция также возвращает Promise, то есть за ожиданием также может следовать асинхронная функция.
API async/await выглядит как сопрограмма и функция приостановки в Kotlin.
Зачем использовать асинхронность?
Скрыть Promise, проще понять
Предположим, мы хотим запросить интерфейс, затем вывести данные ответа и перехватить исключение. В Promise примерно так написано:
function logFetch(url) {
return fetch(url)
.then(response => response.text())
.then(text => {
console.log(text);
}).catch(err => {
console.error('fetch failed', err);
});
}
Если вы используете асинхронную функцию для записи, это будет выглядеть так:
async function logFetch(url) {
try {
const response = await fetch(url);
console.log(await response.text());
}
catch (err) {
console.log('fetch failed', err);
}
}
Хотя количество строк кода такое же, код выглядит более лаконичным, с гораздо меньшей вложенностью, чем s. Запросить данные интерфейса, затем распечатать, как видите, просто.
Как и в только что приведенном примере, если вы не изучили Promise, это не повлияет на вашу способность читать и понимать асинхронный код. Вроде бы ничего, но для масштабного проекта технический уровень персонала неравномерен, и нельзя гарантировать, что все знакомы и понимают Promise.Что нам нужно сделать, так это уменьшить сложность понимания кодируйте как можно больше, чтобы большинство людей могли его увидеть.
Напишите асинхронную логику с синхронными идеями
Самое большое преимущество async/await заключается в том, что мы можем использовать синхронные идеи для написания асинхронной бизнес-логики, поэтому весь код выглядит более понятным. Возьмем пример.
Приведенный выше код относительно прост, давайте возьмем более сложный пример.
Мы хотим получить размер сетевого ресурса, при использовании Promise это может выглядеть так:
function getResponseSize(url) {
return fetch(url).then(response => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(function processResult(result) {
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
})
});
}
Даже если вы выучили промисы, этот код не очень легко понять, не говоря уже о студентах, которые не выучили промисы. Поскольку в середине есть зацикленный процесс, а процесс выполнения асинхронный, не ожидается, что вызов цепочки, который мы изучили ранее, может быть решен. Конечно, вы также можете извлечь его, чтобы сделать код более кратким.
const processResult = (result) =>{
if (result.done) return total;
const value = result.value;
total += value.length;
console.log('Received chunk', value);
return reader.read().then(processResult);
}
function getResponseSize(url) {
return fetch(url).then(response => {
const reader = response.body.getReader();
let total = 0;
return reader.read().then(processResult)
});
}
Но есть еще один способ, едва ли. Но давайте посмотрим, как обрабатываются асинхронные функции.
async function getResponseSize(url) {
const response = await fetch(url);
const reader = response.body.getReader();
let result = await reader.read();
let total = 0;
while (!result.done) {
const value = result.value;
total += value.length;
console.log('Received chunk', value);
// get the next result
result = await reader.read();
}
return total;
}
OHHHHHHHHH
Это выглядит мягче? Поскольку выражения await блокируют выполнение и даже могут напрямую блокировать циклы, общий вид синхронного кода более интуитивно понятен и удобен для чтения.
Остерегайтесь блокировки ожидания
Поскольку AWAIT может блокировать работу асинхронной функции, код больше похож на синхронный код, его легче читать и понимать. Но будьте осторожны с блокировкой AWAIT, потому что некоторые блокировки не нужны, неправильное использование может повлиять на производительность кода.
Если мы хотим объединить сетевые данные с локальными данными, неправильный пример может выглядеть так:
async function combineData(url, file) {
let networkData = await fetch(url)
let fileData = await readeFile(file)
console.log(networkData + fileData)
}
На самом деле нам не нужно ждать, пока будет прочитан один файл, прежде чем читать следующий файл, мы можем прочитать два файла вместе, а затем объединить их после чтения, что может повысить скорость работы кода. Мы можем написать это так:
async function combineData(url, file) {
let fetchPromise = fetch(url)
let readFilePromise = readFile(file)
let networkData = await fetchPromise
let fileData = await readFilePromise
console.log(networkData + fileData)
}
Таким образом, вы можете одновременно выполнять сетевые запросы и читать файлы, что может сэкономить много времени. В основном это делается для того, чтобы воспользоваться функцией, согласно которой Promise выполняется сразу после создания.Студенты, которые не понимают, могут просмотреть содержимое предыдущего раздела.
Конечно, если вы знакомы с Promise, вы можете использовать Promise.all для его обработки напрямую или await с последующим Promise.all, и здесь это обсуждаться не будет.
Обработка исключений
try...catch
В асинхронной функции обработка исключений обычно выполняется с помощью try...catch Если try...catch не выполняется, после отклонения выражения ожидания обещание, возвращаемое асинхронной функцией, будет отклонено.
На самом деле, в сочетании с Promise, если состояние Promise финализируется как reject , а последующее потом не проходит в функцию reject или отсутствует catch , то будет брошено исключение. С этой точки зрения, обертывание выражения ожидания с помощью try...catch в асинхронной функции может заключаться в перехвате исключения и передаче сообщения об отклонении в функцию catch.
Здесь нет примеров.