Функция возвращает оба значения
Другой способ обработки ошибок практикуется в языке Go. Он позволяет async-функции возвращать как ошибку, так и результат. За более подробным описанием направляю вас в эту статью:
Короче говоря, вы можете использовать async-функцию следующим образом:
= await to(UserModel.findById(1));
Лично мне не нравится этот подход, поскольку он привносит стиль Go в JavaScript, который кажется неестественным, но в некоторых случаях это может оказаться весьма полезным.
Использование .catch
И последний способ вызова ошибок, которым мы поделимся здесь, — продолжить использование .
Вспомните функциональность : эта функция будет ждать, пока промис завершит свою работу. Также, пожалуйста, помните, что тоже вернет промис! Поэтому мы можем написать обработку ошибок следующим образом:
// books === undefined if error happens,// since nothing returned in the catch statementlet books = await bookModel.fetchAll() .catch((error) => { console.log(error); });
В этом подходе есть две незначительные проблемы:
4 ответа
8
Если виновником является HtmlWebpackPlugin, вам нужно добавить опцию при создании плагина. Некоторые конфигурации без этой опции заставляют ваш встроенный javascript-код загружаться дважды.
05 дек. 2017, в 04:37
Поделиться
3
Idempotent Babel Polyfill можно импортировать несколько раз
Установить с NPM
Затем импортируйте его
15 апр. 2018, в 03:20
Поделиться
Возможно, вы получаете это косвенно из какого-либо другого модуля babel.
Возможные решения:
- Сделайте все версии модулей babel одинаковыми. Вероятно, ошибка связана с различными версиями babel-polyfil.
- Удалите babel-polyfil из package.json, чтобы он использовался из столпотворение-плагин-преобразование-объектный покой распространения.
ссылка: https://github.com/babel/babel/issues/1019
комментарий от jameslk
11 май 2017, в 12:27
Поделиться
Ещё вопросы
- JQuery AJAX данные не печатаются в родительском элементе
- Приложение Ionic работает в браузере, но не работает на моем устройстве Android
- 1Как вести сенсорный мониторинг при изменении ориентации экрана?
- MySQL-запрос, возвращающий данные, даже если условие ложно
- 1Как добавить массив JSON для объекта JSON в PHP
- 1Как установить положение GeneralPath в JPanel?
- 1Используйте Blob в хранилище данных, но пространство имен не известно Android
- Вызов конструктора копирования из неизвестного класса без нового
- Передача переменных из файла JavaScript на другую HTML-страницу
- Не удается подключиться к Google SQL в приложении Flask, работающем локально
- Записи не сохраняются в базе данных MySQL
- 1Как удалить собственный URL-адрес JSP-запроса?
- 2Создание всплывающей подсказки из приложения только в системном трее
- 2Xamarin.UITest: как получить координаты отображения устройства
- 1Как заполнить сущности Symfony JSON-ответом от удаленного API
- 1Конвертировать объект JavaScript в HTML
- 2«JSON» не определено
- Как исправить сообщение «Typerror is undefined» для функции в JavaScript?
- 1У установки пакетов python есть проблема в md5
- JQuery / JS Предоставление разных результатов между браузерами и между типами обновления
- 1Список списков Python против NumPy
- 1Создание функций в Twig не работает
- 1Эмулятор Android не работает с Mono для Android
- Ошибка утверждения программы C ++ из-за удаления указателя
- Скрыть / показать кнопку «Очистить», когда фильтр был использован
- 1Исключение при модификации во время исполнения
- Проблемы с движком конструктора
- 1объединение значений для большого количества перекрывающихся интервалов словарных ключей
- 1Как сделать Мандельброта Сета быстрее?
- какая разница между DATEDIFF и выполнением вычитания самостоятельно
- 2PostAsJsonAsync не вызывает службу webapi из службы windows, используя анонимный тип, что-то очевидно не так?
- Загрузка CSV-файла в MySQL на сервере
- Некоторые проблемы при написании пользовательских фильтров
- 1Как сделать http-вызов на DialogFlow v2, используя Javascript AJAX-вызов
- Альтернативная реализация символа при динамическом линковании в Linux
- Я хочу только строковое значение из JSON.Stringify
- angularjs ng-include передать значение из контроллера
- 1Как правильно сопоставить объект с базой данных?
- 1Резка дендрограммы / кластерного дерева от SciPy на высоте расстояния
- Отключение нескольких листовых узлов в Angular Treel Control
- substring_index не принимает точный префикс
- Суммирующие кратные C ++ 3 и 5
- HTML DIV противоречит DIV, что он находится в
- 1Растровое изображение в Android
- 1Динамическая диаграмма в Python
- 1Какие из этих операций лучше с точки зрения экономии памяти и процессора? (Ява)
- 1Android — Положение изображения внутри EditText
- Как открыть файл во внешнем редакторе по умолчанию на Qt / Android
- Как разобрать вывод формы?
- Обратная совместимость noexcept (false) для деструкторов
Browser support
One consideration when deciding whether to use async/await is support for older browsers. They are available in modern versions of most browsers, the same as promises; the main support problems come with Internet Explorer and Opera Mini.
If you want to use async/await but are concerned about older browser support, you could consider using the BabelJS library — this allows you to write your applications using the latest JavaScript and let Babel figure out what changes if any are needed for your user’s browsers. On encountering a browser that does not support async/await, Babel’s polyfill can automatically provide fallbacks that work in older browsers.
try…catch
Самый стандартный способ, его же я обычно рекомендую — использовать конструкцию . При ожидании вызова, то есть в случае с любое отклоненное значение будет выброшено как исключение. Вот пример:
class BookModel { fetchAll() { return new Promise((resolve, reject) => { window.setTimeout(() => { reject({'error': 400}) }, 1000); }); }}// async/awaitasync getBooksByAuthorWithAwait(authorId) {try { const books = await bookModel.fetchAll();} catch (error) { console.log(error); // { "error": 400 }}
Error в — это то самое отклоненное значение. После того, как мы поймали исключение, у нас есть несколько способов работы с ним:
- Обработать исключение и вернуть нормальное значение. (Неиспользование выражения в блоке эквивалентно применениюкоторое также является нормальным значением.)
- Выбросить ошибку, если хотите, чтобы функция вызова обработала её. Вы можете либо выбросить простой объект ошибки напрямую, с помощью , что позволит вам использовать функцию в цепочке промисов (другими словами, вы все равно можете вызвать её следующим образом — ); другой вариант — можно обернуть ошибку с помощью объекта например, , что позволит увидеть полную трассировку стека, когда эта ошибка будет отображаться в консоли.
- Отклонить ошибку, например, . Это эквивалентно , поэтому не рекомендуется.
Преимущества использования :
- Простой, традиционный способ. Если у вас есть опыт работы с другими языками, такими как Java или C ++, вам не составит труда понять данную концепцию.
- Дает возможность поместить несколько вызовов в один блок для обработки ошибок в одном месте, если обработка ошибок на каждом шаге не требуется.
В этом подходе есть и один недостаток. Так как поймает любое исключение в блоке, то будут выброшены ошибки, которые в обычных случаях промисами не отлавливаются. Для того, чтобы понять эту идею, взгляните на пример:
class BookModel { fetchAll() { cb(); // note `cb` is undefined and will result an exception return fetch('/books'); }}try { bookModel.fetchAll();} catch(error) { console.log(error); // This will print "cb is not defined"}
Запустите этот код и вы получите ошибку в консоли, черного цвета. Ошибка выводилась с помощью но не самим JavaScript. Иногда это может быть фатальным: если заключен глубоко в ряд вызовов функций, и один из вызовов проглатывает ошибку, тогда будет очень сложно найти неопределенную ошибку, подобную этой.
babel-polyfill
Almost all futuristic JavaScript syntax can be compiled with Babel, but the same is not true for APIs.
For example, the following code has an arrow function that needs to be compiled:
function addAll() { return Array.from(arguments).reduce((a, b) => a + b); }
Which turns into this:
function addAll() { return Array.from(arguments).reduce(function(a, b) { return a + b; }); }
However, this still won’t work everywhere because doesn’t exist in every JavaScript environment.
Babel uses the excellent core-js as its polyfill, along with a customized regenerator runtime for getting generators and async functions working.
To include the Babel polyfill, first install it with npm:
$ npm install --save babel-polyfill
Then simply include the polyfill at the top of any file that requires it:
import "babel-polyfill";
Technical details
The transformer plugin does three things:
- Automatically requires when you use generators/async functions (toggleable with the option).
- Can use for helpers if necessary instead of assuming it will be polyfilled by the user (toggleable with the option)
- Automatically removes the inline Babel helpers and uses the module instead (toggleable with the option).
What does this actually mean though? Basically, you can use built-ins such as , , , etc., as well use all the Babel features that require a polyfill seamlessly, without global pollution, making it extremely suitable for libraries.
Make sure you include as a dependency.
Regenerator aliasing
Whenever you use a generator function or async function:
the following is generated:
This isn’t ideal since it relies on the regenerator runtime being included, which
pollutes the global scope.
With the transformer, however, it is compiled to:
This means that you can use the regenerator runtime without polluting your current environment.
aliasing
Sometimes you may want to use new built-ins such as , , etc. Your only way
to use these is usually to include a globally polluting polyfill.
This is with the option.
The plugin transforms the following:
into the following:
This means is that you can seamlessly use these native built-ins and methods
without worrying about where they come from.
NOTE: Instance methods such as will only work when using .
Helper aliasing
Usually Babel will place helpers at the top of your file to do common tasks to avoid
duplicating the code around in the current file. Sometimes these helpers can get a
little bulky and add unnecessary duplication across files. The
transformer replaces all the helper calls to a module.
That means that the following code:
usually turns into:
the transformer however turns this into:
Переписываем Promises с использованием async/await
Давайте посмотрим на пример из предыдущей статьи:
К этому моменту вы должны понимать как работают Promises, чтобы понять все остальное. Давайте перепишем код используя async/await и оценим разницу.
Согласитесь, что код стал короче и понятнее — больше никаких блоков по всему скрипту!
Так как ключевое слово заставляет функцию вернуть Promise, мы можем использовать гибридный подход:
Можете попрактиковаться самостоятельно, или запустить наш live example (а также source code).
Вы могли заметить, что мы обернули наш код в функцию и сделали её асинхронной с помощью . Это было обязательно — нам надо создать контейнер, внутри которого будет запускаться асинхронный код и будет возможность дождаться его результата с помощью await, не блокируя остальной код нашего скрипта.
Внутри находится код, который слегка напоминает версию на Promise, но есть важные отличия. Вместо того, чтобы писать цепочку блоков мы просто использует ключевое слово перед вызовом promise-based функции и присваиваем результат в переменную. Ключевое слово говорит JavaScript runtime приостановить код в этой строке, не блокируя остальной код скрипта за пределами асинхронной функции. Когда вызов promise-based функции будет готов вернуть результат, выполнение продолжится с этой строки дальше.
Пример:
Значение Promise, которое вернёт будет присвоено переменной только тогда, когда оно будет доступно — парсер делает паузу на данной строке дожидаясь этого момента. Как только значение доступно, парсер переходит к следующей строке, в которой создаётся объект из результата Promise. В этой строке, кстати, также используется , потому что метод также возвращает Promise. Когда результат готов, мы возвращаем его наружу из .
Обратите внимание, когда мы вызываем , она возвращает Promise, поэтому мы можем вызвать на результате, чтобы отобразить его на экране.
К этому моменту вы наверное думаете «Это реально круто!», и вы правы — чем меньше блоков , тем легче читать код. Чтобы обработать ошибки у нас есть несколько вариантов
Чтобы обработать ошибки у нас есть несколько вариантов
Мы можем использовать синхронную структуру с /. Вот изменённая версия первого примера выше:
В блок передаётся объект ошибки, который мы назвали ; мы можем вывести его в консоль, чтобы посмотреть детали: где и почему возникла ошибка.
Если вы хотите использовать гибридный подходы (пример выше), лучше использовать блок после блока вот так:
Так лучше, потому что блок словит ошибки как из асинхронной функции, так и из Promise. Если бы мы использовали блок /, мы бы не словили ошибку, которая произошла в самой функции.
Вы можете посмотреть оба примера на GitHub:
- simple-fetch-async-await-try-catch.html (смотреть source code)
- simple-fetch-async-await-promise-catch.html (смотреть source code)
Await и Promise.all()
Как вы помните, асинхронные функции построены поверх promises, поэтому они совместимы со всеми возможностями последних. Мы легко можем подождать выполнение , присвоить результат в переменную и все это сделать используя синхронный стиль. Опять, вернёмся к примеру, рассмотренному в предыдущей статье. Откройте пример в соседней вкладке, чтобы лучше понять разницу.
Версия с async/await (смотрите live demo и source code), сейчас выглядит так:
Вы видите, что мы легко изменили функцию в асинхронный вариант. Взгляните на строку с :
С помощью мы ждём массив результатов всех трёх Promises и присваиваем его в переменную . Это асинхронный код, но он написан в синхронном стиле, за счёт чего он гораздо читабельнее.
Мы должны обернуть весь код в синхронную функцию, , и мы не сильно сэкономили на количестве кода, но мы извлекли код блока , за счёт чего наш код стал гораздо чище.
Для обработки ошибок мы добавили блок для функции ; Это позволило нам отловить ошибки в обоих функциях.
Примечание: Мы также можем использовать синхронный блок внутри асинхронной функции, вместо асинхронного , чтобы получить информацию о результате нашей операции — смотрите в действии в нашем live example (смотрите source code).
Инструменты для статического анализа
Новейшие стандарты добавляют в язык новый синтаксис и инструменты статического анализа только начинают пользоваться этими преимуществами.
Линтинг (инструменты для проверки кода)
Сначала установите и .
$ npm install --save-dev eslint babel-eslint
Затем создайте или используйте существующий файл в вашем проекте и установите как .
{ + "parser": "babel-eslint", "rules": { ... } }
Теперь добавьте -задачу в ваш npm скрипт:
{ "name": "my-module", "scripts": { + "lint": "eslint my-files.js" }, "devDependencies": { "babel-eslint": "...", "eslint": "..." } }
Затем просто запустите данную задачу, — вы все настроили.
$ npm run lint
Стиль кода
JSCS is an extremely popular tool for taking linting a step further into checking the style of the code itself. A core maintainer of both the Babel and JSCS projects (@hzoo) maintains an official integration with JSCS.
Even better, this integration now lives within JSCS itself under the option. So integrating Babel is as easy as:
From the cli, or adding the option to your file.
{ "preset": "airbnb", + "esnext": true }
Documentation
Documentation.js uses Babel behind the scenes to support all of the latest syntax including Flow annotations in order to declare the types in your code.
babel-core
Если вы хотите использовать Babel программно, то подключите пакет .
Установите .
$ npm install babel-core
var babel = require("babel-core");
Если вам нужно скомпилировать строку JavaScript кода, можно воспользоваться .
babel.transform("code();", options); // => { code, map, ast }
Если вы работаете с файлами, вы можете использовать асинхронные api:
babel.transformFile("filename.js", options, function(err, result) { result; // => { code, map, ast } });
Либо синхронные api:
babel.transformFileSync("filename.js", options); // => { code, map, ast }
Если у вас уже, по какой-то причине, есть Babel AST, вы можете преобразовывать непосредственно AST.
babel.transformFromAst(ast, code, options); // => { code, map, ast }
Настройка Babel
Как вы могли заметить, сам по себе Babel при запуске только копирует Javascript файлы из одного места в другое.
Это происходит потому что мы еще не сказали Babel что следует делать.
Вы можете снабдить Babel необходимыми инструкциями, установив плагины либо пресеты (группы плагинов).
Await
Синтаксис:
Ключевое слово заставит интерпретатор JavaScript ждать до тех пор, пока промис справа от не выполнится. После чего оно вернёт его результат, и выполнение кода продолжится.
В этом примере промис успешно выполнится через 1 секунду:
В данном примере выполнение функции остановится на строке до тех пор, пока промис не выполнится. Это произойдёт через секунду после запуска функции. После чего в переменную будет записан результат выполнения промиса, и браузер отобразит alert-окно «готово!».
Обратите внимание, хотя и заставляет JavaScript дожидаться выполнения промиса, это не отнимает ресурсов процессора. Пока промис не выполнится, JS-движок может заниматься другими задачами: выполнять прочие скрипты, обрабатывать события и т.п
По сути, это просто «синтаксический сахар» для получения результата промиса, более наглядный, чем .
нельзя использовать в обычных функциях
Если мы попробуем использовать внутри функции, объявленной без , получим синтаксическую ошибку:
Ошибки не будет, если мы укажем ключевое слово перед объявлением функции. Как было сказано раньше, можно использовать только внутри –функций.
Давайте перепишем пример из раздела Цепочка промисов с помощью :
- Нам нужно заменить вызовы на .
- И добавить ключевое слово перед объявлением функции.
Получилось очень просто и читаемо, правда? Гораздо лучше, чем раньше.
нельзя использовать на верхнем уровне вложенности
Программисты, узнав об , часто пытаются использовать эту возможность на верхнем уровне вложенности (вне тела функции). Но из-за того, что работает только внутри –функций, так сделать не получится:
Можно обернуть этот код в анонимную –функцию, тогда всё заработает:
работает с «thenable»–объектами
Как и , позволяет работать с промис–совместимыми объектами. Идея в том, что если у объекта можно вызвать метод , этого достаточно, чтобы использовать его с .
В примере ниже, экземпляры класса будут работать вместе с :
Когда получает объект с , не являющийся промисом, JavaScript автоматически запускает этот метод, передавая ему аргументы – встроенные функции и . Затем приостановит дальнейшее выполнение кода, пока любая из этих функций не будет вызвана (в примере это строка ). После чего выполнение кода продолжится с результатом или соответственно.
Асинхронные методы классов
Для объявления асинхронного метода достаточно написать перед именем:
Как и в случае с асинхронными функциями, такой метод гарантированно возвращает промис, и в его теле можно использовать .
Async Await JS
Конструкция также использует объекты promise для создания асинхронных запросов, но делает написание нашего кода заметно проще.
Async await позволяет писать асинхронный код, так как будто он является синхронным.
Для использования нужно указать, что наша функция будет содержать асинхронный код, путем добавления слова .
1asyncfunctioneatBreakfast(){}
Далее, внутри функции нужно отметить словом те строчки, в которых содержится асинхронный код.
1 2constgetToast=()=>{ 3returnnewPromise((resolve, reject)=>{ 4setTimeout(()=>{ 5resolve('Ваш тост готов!'); 6},1000); 7}); 8}; 9 10 11constgetCoffee=()=>{ 12returnnewPromise((resolve, reject)=>{ 13setTimeout(()=>{ 14resolve('Ваш кофе готов!'); 15},2000); 16}); 17}; 18 19 20constbreakfast=asyncfunction(){ 21 22const toast =awaitgetToast(); 23 24const coffee =awaitgetCoffee(); 25 26 27constmyToast, myCoffee=awaitPromise.all(toast, coffee); 28console.log(myToast, myCoffee); 29 30}; 31 32breakfast();
babel-runtime
In order to implement details of ECMAScript specs, Babel will use «helper» methods in order to keep the generated code clean.
Since these helpers can get pretty long, and they get added to the top of every file you can move them into a single «runtime» which gets required.
Start by installing and :
$ npm install --save-dev babel-plugin-transform-runtime $ npm install --save babel-runtime
Then update your :
{ "plugins": }
Now Babel will compile code like the following:
class Foo { method() {} }
Into this:
import _classCallCheck from "babel-runtime/helpers/classCallCheck"; import _createClass from "babel-runtime/helpers/createClass"; let Foo = function () { function Foo() { _classCallCheck(this, Foo); } _createClass(Foo, { key: "method", value: function method() {} }); return Foo; }();
Rather than putting the and helpers in every single file where they are needed.
Настройка Babel (Продвинутый уровень)
Most people can get by using Babel with just the built-in presets, but Babel exposes much finer-grained power than that.
регенератор Время выполнения не определено в разработке vue
http-equiv=»Content-Type» content=»text/html;charset=UTF-8″>style=»clear:both;»>
Мой проект — это проект, созданный с помощью скаффолда vue-cil, предоставляемого vue, но когда я использую async / await в проекте, это отображается при компиляции кодаНеисправность, я проверил информацию, потому что вы не использовали ее в своем проектеИзмените es6 на es5
Интеллектуальная рекомендация
1. Для реальных сигналов (для понимания): A (ω) является соотношением амплитуды выходного сигнала и амплитуды входного сигнала, называемого частотой амплитуды. Φ (ω) — это разница межд…
Один. вести Многие люди задавали некоторые вопросы о создании проекта Flex + LCDS (FDS) в сообщениях и группах. Из-за операции ее трудно четко объяснить, поэтому я написал простой учебник (я обещал эт…
package com.example.phonehttp; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.widget.ScrollView; import android.widget.TextView; public class MainActi…
Он предназначен для реализации подкласса того же родительского класса с родительским классом. Полиморфизм Один и тот же ссылочный тип использует разные экземпляры для выполнения разных операций; Идея …
тема: Объедините два упорядоченных слоя в новый заказанный список и возврат. Новый список состоит из всех узлов двух связанных списков, данных сплавным. Пример: Анализ: два связанных списка состоит в …
Вам также может понравиться
D. Самая ценная строка Пример ввода 2 2 aa aaa 2 b c Образец вывода aaa c На самом деле, будучи задетым этим вопросом, вы должны быть осторожны. После инвертирования строки, если две строки имеют один…
Given a 2D integer matrix M representing the gray scale of an image, you need to design a smoother to make the gray scale of each cell becomes the average gray scale (rounding down) of all the 8 surro…
calc () может быть очень незнакомым для всех, и трудно поверить, что calc () является частью CSS. Поскольку он выглядит как функция, почему он появляется в CSS, поскольку это функция? Этот момент такж…
Основываясь на дереве регрессии, сформированном CART, а также на предварительной и последующей обрезке дерева, код выглядит следующим образом:…
Откат Обновление в режиме онлайн с версии Centos (CentOS Linux версии 7.3.1611 (Core) до CentOS Linux версии 7.5.1804 (Core)) # ошибка соединения yum-ssh после обновления yexpected key exchange group …
Awaiting a Promise.all()
async/await is built on top of promises, so it’s compatible with all the features offered by promises. This includes — you can quite happily await a call to get all the results returned into a variable in a way that looks like simple synchronous code. Again, let’s return to an example we saw in our previous article. Keep it open in a separate tab so you can compare and contrast with the new version shown below.
Converting this to async/await (see live demo and source code), this now looks like so:
You’ll see that the function has been converted easily into an async function with just a few changes. See the line:
By using here we are able to get all the results of the three promises returned into the array, when they are all available, in a way that looks very much like sync code. We’ve had to wrap all the code in a new async function, , and we’ve not reduced the code by a lot of lines, but being able to move the bulk of the code out of the block provides a nice, useful simplification, leaving us with a much more readable program.
For error handling, we’ve included a block on our call; this will handle errors occurring in both functions.
Note: It is also possible to use a sync block within an async function, in place of a async block, to show a final report on how the operation went — you can see this in action in our live example (see also the source code).
The basics of async/await
There are two parts to using async/await in your code.
First of all we have the keyword, which you put in front of a function declaration to turn it into an async function. An async function is a function that knows how to expect the possibility of the keyword being used to invoke asynchronous code.
Try typing the following lines into your browser’s JS console:
The function returns «Hello» — nothing special, right?
But what if we turn this into an async function? Try the following:
Ah. Invoking the function now returns a promise. This is one of the traits of async functions — their return values are guaranteed to be converted to promises.
You can also create an async function expression, like so:
And you can use arrow functions:
These all do basically the same thing.
To actually consume the value returned when the promise fulfills, since it is returning a promise, we could use a block:
or even just shorthand such as
Like we saw in the last article.
So the keyword is added to functions to tell them to return a promise rather than directly returning the value.
The advantage of an async function only becomes apparent when you combine it with the await keyword. only works inside async functions within regular JavaScript code, however it can be used on its own with JavaScript modules.
can be put in front of any async promise-based function to pause your code on that line until the promise fulfills, then return the resulting value.
You can use when calling any function that returns a Promise, including web API functions.
Here is a trivial example:
Of course, the above example is not very useful, although it does serve to illustrate the syntax. Let’s move on and look at a real example.
Конфигурируйте контекст
Ранее в этой статье я кратко пояснил, как по умолчанию захватывается «контекст», когда ожидается незавершенный Task, и что этот захваченный контекст используется для возобновления async-метода. Пример на рис. 2 показывает, как возобновление с использованием захваченного контекста входит в противоречие с синхронным блокированием и вызывает взаимоблокировку. Это поведение, связанное с контекстом, может создать еще одну проблему — на этот раз с производительностью. По мере роста асинхронных GUI-приложений вы можете обнаружить, что многие малые части всех async-методов используют в качестве контекста GUI-поток. Это приведет к замедлению работы.
Чтобы ослабить остроту этой проблемы, по возможности ожидайте результат ConfigureAwait. Следующий фрагмент кода демонстрирует поведение контекста по умолчанию и применение ConfigureAwait:
Используя ConfigureAwait, вы разрешаете некую долю параллелизма: часть асинхронного кода может выполняться параллельно с GUI-потоком, а не только в нем.
Помимо производительности, ConfigureAwait имеет еще один важный аспект: он помогает избегать взаимоблокировок. Снова взгляните на рис. 2: если добавить ConfigureAwait(false) к строке кода в DelayAsync, взаимоблокировка исключается. На этот раз, когда выражение await завершается, оно пытается выполнить остальную часть async-метода в контексте пула потоков. Метод получает возможность выполнить свою работу, т. е. закончить свою возвращаемую задачу, и взаимоблокировки нет. Эта методика особенно полезна, если вам нужно постепенно преобразовывать свое приложение из синхронного в асинхронное.
Если вы можете после какой-то точки использовать ConfigureAwait внутри метода, я рекомендую вам делать это для каждого await-выражения в этом методе. Вспомните, что контекст захватывается, только если ожидается незавершенный Task; если Task уже выполнен, контекст не захватывается. Некоторые задачи могут выполняться быстрее, чем ожидалось, на другом оборудовании и в других сетях, и вам потребуется корректно обрабатывать возвращенную задачу, которая была выполнена до начала ожидания. Модифицированный пример показан на рис. 4.
Рис. 4. Обработка возвращенной задачи, которая выполнена до начала ожидания
Не используйте ConfigureAwait, если у вас есть код, которому после выражения await в методе требуется контекст. В случае GUI-приложений это любой код, который манипулирует GUI-элементами, пише в свойства, связанные с данными, или зависит от специфичного для GUI типа вроде Dispatcher/CoreDispatcher. В случае приложений ASP.NET это любой код, который использует HttpContext.Current или формирует ASP.NET-ответ, включая выражения return в операциях контроллера. На рис. 5 демонстрируется один из распространенных шаблонов в GUI-приложениях: асинхронный обработчик событий отключает свой элемент управления где-то в начале метода, выполняет какие-то выражения await, а затем вновь включает этот элемент управления в конце обработчика; данный обработчик событий не может отказаться от контекста, так как ему нужно заново включить свой элемент управления.
Рис. 5. Асинхронный обработчик событий, который отключает, а затем включает свой элемент управления
У каждого async-метода свой контекст, поэтому, если один async-метод вызывает другой async-метод, их контексты независимы. На рис. 6 показана небольшая модификация варианта с рис. 5.
Рис. 6. У каждого асинхронного метода свой контекст
Контекстно-независимый код обеспечивает более высокую степень повторного использования. Попробуйте создать барьер в своем коде между контекстно-зависимым и контекстно-независимым кодом и свести к минимуму контекстно-зависимый код. На рис. 6 я посоветовал поместить всю базовую логику обработчика событий в тестируемый и контекстно-независимый async Task-метод, оставив в контекстно-зависимом обработчике событий лишь самый минимум кода. Даже если вы пишете приложение ASP.NET и у вас есть базовая библиотека, которая потенциально может использоваться совместно с настольными приложениями, подумайте о применении ConfigureAwait в библиотечном коде.
Подводя итог по третьему «руководящему принципу», становится ясным, что вы должны по возможности использовать ConfigureAwait. Контекстно-независимый код работает быстрее в GUI-приложениях и является полезной методикой предотвращения взаимоблокировок при работе с частично асинхронной кодовой базой. Исключения из этого принципа — методы, требующие контекста.