У меня в последнее время много дел, давно не учился, да и сам влепилмаленький сайт, добро пожаловать звезда.
JavaScript async/await: The Good Part, Pitfalls and How to Use
Представлено ES7async/await
Функции — это серьезное улучшение асинхронного программирования в JS. Это дает нам возможность получать ресурсы асинхронно, используя стиль синхронного кода, не блокируя основной поток. Конечно, его использование также требует некоторых навыков, и в этой статье мы рассмотрим его с разных сторон.async/await
, чтобы показать вам, как использовать их правильно и эффективно.
async/await
преимущество
Его самое большое преимущество заключается в том, что он обеспечивает синхронный стиль кода. См. код:
// async/await
async getBooksByAuthorWithAwait(authorId) {
const books = await bookModel.fetchAll();
return books.filter(b => b.authorId === authorId);
}
// promise
getBooksByAuthorWithPromise(authorId) {
return bookModel.fetchAll()
.then(books => books.filter(b => b.authorId === authorId));
}
очевидно,async/await
соотношение версийpromise
Версия проще и понятнее. если вы проигнорируетеawait
ключевое слово, то код похож на другие синхронные языки программирования, такие какPython
.
Преимущество не только в удобочитаемости,async/await
Уже изначально поддерживается браузерами. Сегодня все основные браузеры имеютполностью поддерживаю.
Встроенная поддержка означает, что вам не нужноконвертироватькода и, что более важно, облегчает отладку. когда вы находитесь в функцииawait
Когда вы нажмете точку останова на строке кода и перейдете к следующей строке, вы обнаружите, что отладчикbookModel.fetchAll()
Во время операции он ненадолго остановился, а потом действительно вошел в.filter
строка кода! это лучше, чемpromise
Отладка проще, потому что вам нужно.fliter
Нажмите еще одну точку останова в строке кода.
Еще одно малозаметное преимущество заключается в том, чтоasync
ключевые слова. это показываетgetBooksByAuthorWithAwait()
Возвращаемое значение функции должно бытьpromise
, поэтому вызывающая сторона может использоватьgetBooksByAuthorWithAwait().then(...)
или безопасно использоватьawait getBooksByAuthorWithAwait()
. См. код (плохая практика!):
getBooksByAuthorWithPromise(authorId) {
if (!authorId) {
return null;
}
return bookModel.fetchAll()
.then(books => books.filter(b => b.authorId === authorId));
}
}
Во фрагменте кода вышеgetBooksByAuthorWithPromise
может вернутьpromise
(нормальный) илиnull
значение (исключение), и в этом случае вызывающая сторона не может безопасно использовать.then()
. и имеютasync
заявление, чтобы избежать этой неопределенности.
async/await
иногда вводящий в заблуждение
Некоторые статьи будут сравниватьasync/await
а такжеpromise
и утверждают, что это следующее поколениеJS
Асинхронное программирование, с которым я не согласен.async/await
КонечноУлучшение, но это всего лишь синтаксический сахар, и он не изменит полностью наш стиль программирования.
По сути,async
функция по-прежнемуpromises
. при правильном использованииasync
Прежде чем вам нужно понятьpromise
, вероятно, вы используетеasync
также нужно использоватьpromise
.
Просмотрите код вышеgetBooksByAuthorWithAwait()
а такжеgetBooksByAuthorWithPromises()
функции, они не только функционируют точно так же, но и имеют такой же интерфейс.
Это означает, что вызов напрямуюgetBooksByAuthorWithAwait()
вернетpromise
.
Это не обязательно плохо, и большинство людей думают, чтоawait
Идея, что асинхронную функцию можно сделать синхронной, неверна.
async/await
ловушка
где мы используемasync/await
Какие ошибки будут допущены? Вот некоторые общие моменты.
слишком синхронизировано
несмотря на то чтоawait
может заставить наш код выглядеть синхронным, но имейте в виду, что они все еще асинхронны, поэтому стоит обратить внимание на код, чтобы избежать слишком синхронного.
async getBooksAndAuthor(authorId) {
const books = await bookModel.fetchAll();
const author = await authorModel.fetch(authorId);
return {
author,
books: books.filter(book => book.authorId === authorId),
};
}
Этот код выглядит хорошо, но он неверен.
-
await bookModel.fetchAll()
будет ждатьfetchAll()
вернуть - Незамедлительно после
await authorModel.fetch(authorId)
будет называться
уведомлениеauthorModel.fetch(authorId)
не зависит отbookModel.fetchAll()
результатов, ведь их можно выполнять параллельно!await
приведет к двум функциям串行执行
, а время выполнения будет больше, чем并行执行
длинная.
Это правильный способ сделать это:
async getBooksAndAuthor(authorId) {
const bookPromise = bookModel.fetchAll();
const authorPromise = authorModel.fetch(authorId);
const book = await bookPromise;
const author = await authorPromise;
return {
author,
books: books.filter(book => book.authorId === authorId),
};
}
И если вы хотите получить все элементы списка по очереди, вам придется полагаться наpromises
:
async getAuthors(authorIds) {
// 错误,这会导致`串行执行`
// const authors = _.map(
// authorIds,
// id => await authorModel.fetch(id));
// 正确
const promises = _.map(authorIds, id => authorModel.fetch(id));
const authors = await Promise.all(promises);
}
Короче говоря, вам все равно нужно относиться к рабочему процессу как к асинхронному и пытаться использоватьawait
для написания синхронного кода. В более сложных рабочих процессах используйте напрямуюpromise
Может быть удобнее.
обработка ошибок
комбинироватьpromises
, асинхронная функция имеет только два возможных возвращаемых значения:resolve值
а такжеreject值
, то мы можем использовать.then()
справляться с обычными ситуациями,.catch()
Обрабатывать исключения. ноasync/await
Обработка ошибок требует некоторого навыка.
try...catch
Самый распространенный (и я рекомендую) способ - использоватьtry..catch
. когдаawait
Во время операции любоеreject值
будет выброшено как исключение. См. код:
class BookModel {
fetchAll() {
return new Promise((resolve, reject) => {
window.setTimeout(() => { reject({'error': 400}) }, 1000);
});
}
}
// async/await
async getBooksByAuthorWithAwait(authorId) {
try {
const books = await bookModel.fetchAll();
} catch (error) {
console.log(error); // { "error": 400 }
}
Объект ошибки вывода точноreject值
. После перехвата исключений мы можем обработать их, используя:
- Обработать исключение и вернуть нормальное значение (в
catch
кодовые блоки не используютсяreturn
заявление эквивалентноreturn undefined;
, конечно, это нормальное значение). - Бросьте, если вы хотите, чтобы вызывающий объект обрабатывал исключение. Вы можете напрямую выбросить объект исключения, например
throw error
, что позволяет вамasync getBooksByAuthorWithAwait()
функция для использованияpromise
цепные операции (т.е.:getBooksByAuthorWithAwait().then(...).catch(error => ...)
); или используйтеError
object обертывает ваш объект ошибки, напримерthrow new Error(error)
, поэтому вы можете увидеть полный журнал стека при просмотре ошибки в консоли. -
reject
объект ошибки, напримерreturn Promise.reject(error)
. Это эквивалентно первому подходу, поэтому не рекомендуется.
использоватьtry...catch
Преимущества заключаются в следующем:
- Простой, традиционный, если у вас есть такие вещи, как
Java
илиC++
Опыт программирования, легко понять. - в
try...catch
В блоке кода вы можетеtry
Блоки кода переносятся на несколько строкawait
заявление, и если предварительная обработка ошибок не требуется, вы можете сделать это в одном месте (т.е.catch
блок кода) для обработки ошибок.
Эта схема все же имеет свои недостатки,try...catch
Может отловить все ошибки в блоке кода, в том числе те, которые неpromises
Ошибки пойманы. См. код:
class BookModel {
fetchAll() {
cb(); // `cb` 因为没有被定义所有会导致异常
return fetch('/books');
}
}
try {
bookModel.fetchAll();
} catch(error) {
console.log(error); // 这里打印 "cb is not defined"
}
Запустите этот код, и вы получите в консолиReferenceError: cb is not defined
Вывод информации черным шрифтом. Вы должны знать, что ошибка здесь черезconsole.log()
вывод, неJS
сама бросает(JS
Ошибки выделены красным шрифтом). Иногда это может быть фатальным: еслиBookModel
Будучи глубоко вложенными и обернутыми вызовами некоторых других функций, один из которых проглатывает исключение, становится крайне сложно найти в примере такую ошибку.
заставить функцию возвращать все значения
кGo
Языковая эвристика, еще один способ обработки ошибок — разрешитьasync
возврат функции异常
а также结果
два значения (см.How to write async await without try-catch blocks in Javascript), то есть можно использоватьasync
функция:
[err, user] = await to(UserModel.findById(1));
Я лично не рекомендую использовать эту реализацию, поскольку она ставитGo
языковой стиль принесJS
, что кажется мне неестественным, но в отдельных случаях крайне уместно использовать.
использовать.catch()
Последний метод заключается в продолжении использования.catch()
.
передуматьawait
что он делает: он ждетpromise
выполнить работу, также помнитеpromise.catch()
также вернетpromise
! Таким образом, мы можем обрабатывать ошибки с помощью этого:
// 如果发生异常,但是 catch 语句没有显示返回,那么 books === undefined
let books = await bookModel.fetchAll()
.catch((error) => { console.log(error); });
У этой реализации есть два недостатка:
- это
promise
а такжеasync
функция перемешивания. тебе нужно понятьpromise
понять это. - Обработка ошибок перед возвратом, что не очень интуитивно понятно.
В заключение
ES7
изasync/await
пара функцийJS
Асинхронное программирование — это огромное улучшение. Это делает код более читабельным и упрощает отладку. Но чтобы правильно ими пользоваться, нужно досконально пониматьpromise
. Поскольку это всего лишь синтаксический сахар, технология, на которую он опирается, по-прежнему актуальна.promise
.