Javascript await async: Промисы, async/await
Современный асинхронный JavaScript с async/await
Вступление
JavaScript за очень короткое время превратился из обратных вызовов в promises (ES2015), а после выхода ES2017 асинхронный JavaScript стал еще проще с синтаксисом async/await
.
Асинхронные функции представляют собой комбинацию промисов и генераторов, и в основном являются абстракцией более высокого уровня (по сравнению с промисами). Позволь напомнить что: async/await
построен на промисах.
Почему был введен async/await
async/await
сокращают шаблон промисов.
Когда Promises были введены в ES2015, они предназначались для решения проблемы с асинхронным кодом, и им это удалось, но за 2 года, в течение которых ES2015 и ES2017 были разделены, стало ясно, что промисы не могут быть окончательным решением.
Промисы были введены для решения известной проблемы “ад обратных вызовов”, но в то же время они же ввели сложность сами по себе и сложность синтаксиса.
Это были хорошие примитивы, вокруг которых разработчикам был представлен лучший синтаксис, поэтому, когда настало время, мы получили асинхронные функции.
async/await
делают код похожим на синхронный, но он асинхронный и не блокирующий.
Как он устроен
Любая функция, которая возвращает промис, имеет API, основанный на промисах, и может использоваться взаимозаменяемо либо с then
, then
, catch
, либо с async/await
.
В JavaScript мы часто слышим асинхронный и синхронный, так давай кратко рассмотрим, что они на самом деле означают. Когда мы выполняешь что-то синхронно, мы ждем, пока задача будет выполнена, прежде чем перейти к другой и продолжить работу с остальной частью кода. Когда мы делаем что-то асинхронно, мы запускаем задачу, но затем сразу переходим к следующему фактическому значению. Вот почему мы не можем сделать что-то подобное:
const res = fetch("https://api. github.com/users/oleksiimyzgin");
console.log(res);
Причина, по которой ответ будет равен промису, а не фактическому значению, заключается в том, что большинство вещей в JavaScript ничего не ждут. Это означает что он выполнит fetch()
, однако не будет приостанавливать весь JavaScript и ждать пока промис вернется. Это то, что называется блокировкой в JavaScript: когда мы блокируем выполнение остальной части JavaScript, пока ждем выполнения начавшейся задачи. Почти всё в JavaScript является асинхронным. Это здорово, но это становится проблемой, когда нужно контролировать поток. Например когда мы хотим, чтобы сначала что-то произошло, затем разобраться с первым ответом, а затем со вторым ответом. Точно так же, как мы сделали с промисами, где нам нужно было ждать. В итоге мы получили цепочку с then()
, then()
, then()
, then()
, один за другим.
Вот тут и появляется async/await
, и поэтому он называется async/await
, потому что нам нужно сначала создать асинхронную async
функцию, а затем внутри этой функции мы ожидаем await
значения. async/await
просто построен на основе промисов. Это не альтернатива промисам и это не совсем другой способ. Чтобы использовать async/await
у нас должна быть функция, основанная на промисах. Важно знать, что await
доступен только в асинхронной функции — await
нельзя выполнить в открытом виде (вне асинхронной функции). Это нужно сделать в функции, помеченной как асинхронная async
. Что бы сделать функцию асинхронной, нужно просто добавить слово async
перед словом function
.
Асинхронная функция возвращает промис, как в этом примере:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve("I did something"), 3000);
});
};
Перед тем как вызвать функцию doSomethingAsync
, тебе нужно добавить await
перед ее вызовом: таким образом вызывающий код останавливается, пока промис не будет resolved
или rejected
. Важно: клиентская функция должна быть определена как асинхронная async
. Например:
const doSomething = async () => {
console.log(await doSomethingAsync());
};
Быстрый пример
Это простой пример использования async/await
для асинхронного запуска функции:
const doSomethingAsync = () => {
return new Promise(resolve => {
setTimeout(() => resolve("I did something"), 3000);
});
};
const doSomething = async () => {
console.log(await doSomethingAsync());
};
console.log("Before");
doSomething();
console.log("After");
Приведенный выше код выведет на консоль браузера следующее:
Before
After
I did something // через 3s
Так происходит потому, что мы делаем resolve
и выводим его значение 'I did something'
в консоль только через 3 секунды.
Асинхронные функции возвращают промис
Добавление ключевого слова async
к любой функции означает, что функция вернет промис, даже если она не делает этого явно.
Вот почему этот код действителен:
const aFunction = async () => {
return "test";
};
aFunction().then(alert);
И этот так же:
const aFunction = async () => {
return Promise.resolve("test");
};
aFunction().then(alert);
Более читабельный код
Как мы можем видеть в приведенном выше примере, наш код выглядит очень просто. Так как это очень простой пример, то основные преимущества станут видимы, когда код будет намного сложнее.
Например, вот как мы можем получить JSON и разпарсить его, используя промис:
const getFirstUserData = () => {
return fetch("/users.json")
.then(response => response.json())
.then(users => users[0])
.then(user => fetch(`/users/${user.name}`))
.then(userResponse => userResponse.json());
};
getFirstUserData();
И вот та же функциональность, предоставляемая с помощью async/await
:
const getFirstUserData = async () => {
const response = await fetch("/users. json");
const users = await response.json();
const user = users[0];
const userResponse = await fetch(`/users/${user.name}`);
const userData = await userResponse.json();
return userData;
};
getFirstUserData();
Как мы можем увидеть код с async/await
выглядит намного читабельнее.
Несколько асинхронных функций в серии
Асинхронные функции могут быть легко соединены в цепочку, а синтаксис гораздо более читабелен, чем у промисов:
async function go() {
const p1 = await fetch("https://api.github.com/users/oleksiimyzgin");
const p2 = await fetch("https://api.github.com/users/leoyats");
}
go();
В приведенном выше примере, мы сначала ждем пока выполнится p1
, а потом уже p2
. Что бы не терять время мы можем запустить их одновременно и подождать пока они все вернутся. Например:
async function go() {
const p1 = fetch("https://api.github.com/users/oleksiimyzgin");
const p2 = fetch("https://api. github.com/users/leoyats");
const res = await Promise.all([p1, p2]);
console.log("res", res);
}
go();
Более простая отладка
Отладка промисов сложна, так как отладчик не перешагнет асинхронный код.
В async/await
сделать это легко, потому что для компилятора он похож на синхронный код.
Используй try catch внутри функции
Обработка ошибок в async/await
на самом деле проста, хотя в коде она выглядит не так красиво. Первый способ заключается в том, что бы обвернуть все или одну из await
функций, которые нужно подождать, в блок try-catch
. Например:
const getFirstUserData = async () => {
try {
const response = await fetch("/users.json");
const users = await response.json();
const user = users[0];
const userResponse = await fetch(`/users/${user.name}`);
const userData = await userResponse.json();
return userData;
} catch (err) {
console. error("Something went wrong");
console.error(err);
}
};
getFirstUserData();
Итак, по сути, мы говорим: попробуй, сделай весь этот код внутри фигурных скобок, и если что-то случится не так, то мы поймаем ошибку в catch(err)
и выведем ее в консоль через console.error
.
Использовать catch для каждого await
Поскольку каждое выражение await
возвращает promise, мы можем добавить к каждой строке catch
, как показано ниже.
const getFirstUserData = async () => {
const response = await fetch("/users.json").catch(err =>
console.error("Something went wrong when fetching data")
);
const users = await response
.json()
.catch(err => console.error("Something went wrong when parsing data"));
};
getFirstUserData();
Использовать catch для всей асинхронной функции
Еще один вариант — использовать catch(err)
для всей асинхронной функции. Например:
const getFirstUserData = async () => {
const response = await fetch('/users. json');
const users = await response.json();
const user = users[0]
const userResponse = await fetch(`/users/${user.name}`);
const userData = await userResponse.json();
return userData;
}
getFirstUserData()
.catch(err) {
console.error('Something went wrong');
console.error(err);
};
Этот вариант отлично подходит для случаев, когда нужно обработать все ошибки одинаково — не нужно писать catch
для каждой await
функции.
Async/Await | bxnotes
Рассмотрим Promise функцию, которая вычисляет число увеличенное на 1 через 1 секунду:
function plusOneAfter1Second(n) {
return new Promise(resolve => {
setTimeout(() => {
resolve(n + 1);
}, 1000);
});
}
Допустим, нужно произвести цепочку последовательных вычислений с доступом к промежуточным результатам вычислений. Чтобы лучше понять причину ввода дополнительного синтаксиса Async/Await рассмотрим, как бы данная задача решалась с помощью Promise.
Такой код работать не будет:
var a = plusOneAfter1Second(6)
var b = plusOneAfter1Second(a)
var c = plusOneAfter1Second(b)
console.log([a, b, c]);
С помощью цепочки Promise мы можем последовательно преобразовать входные данные, но в финальной функции, при таком подходе, нельзя использовать промежуточные результаты, только конечный:
plusOneAfter1Second(6)
.then(n1 => plusOneAfter1Second(n1))
.then(n2 => plusOneAfter1Second(n2))
.then(n3 => plusOneAfter1Second(n3))
.then((n1, n2, n3) => console.log([n1, n2, n3]))
Для того, чтобы получить промежуточные результаты, придется использовать малопонятную конструкцию вида:
function addFrom(a){
return new Promise(resolve => {
plusOneAfter1Second(a).then((b) => {
plusOneAfter1Second(b).then((c) => {
plusOneAfter1Second(c).then((d) => {
resolve(console.log([a, b, c, d]));
})
})
})
});
}
console. log(addFrom(6));
Для решения данной проблемы и более удобной работы с Promise, был введен синтаксис Async/Await. Так как Async/Await работает с Promise, мы можем использовать изначальную функцию, возвращающую Promise:
async function chainAsync(n) {
const a = await plusOneAfter1Second(n);
const b = await plusOneAfter1Second(a);
const c = await plusOneAfter1Second(b);
return [n, a, b, c];
}
chainAsync(5).then(result => {
console.log(result);
});
Данный синтаксис намного удобнее, чем пример с Promise выше. Таким образом, Async/Await функции делают код чище, лаконичней и понятнее.
Конкурентные функции
При использовании await важно правильно спроектировать программу, разделив её на блоки, которые должны выполняться последовательно и параллельно.
Принцип данного разделения в том, чтобы сгруппировать зависимые части программы в async функции. В итоге должны получиться несколько независимых друг от друга async функций, которые могут выполняться конкурентно, так как их работа не зависит друг от друга.
async function selectCarColor() {
const colors = await getCarColors()
const chosenColor = chooseColor(colors)
await saveColor(chosenColor)
}
async function selectCarType() {
const types = await getCarTypes()
const chosenType = chooseCarType(types)
await saveType(chosenType)
}
(async () => {
const colorPromise = selectCarColor()
const typePromise = selectCarType()
const color = await colorPromise
const type = await typePromise
buildCar()
})()
(async () => {
Promise.all([selectCarColor(), selectCarType()]).then(buildCar)
})()
Таким образом, функции selectCarColor()
и selectCarType()
выполняются параллельно, а не последовательно. Далее программа ожидает их успешного завершения и запускает финальную функцию buildCar()
.
Запуск неизвестного числа Promise
Задачу, когда есть неизвестное число элементов, каждый из которых нужно передать в асинхронную функцию, легко решить с помощью метода Promise. all()
. Данный метод ожидает конкурентное завершение массива Promise функций.
async function doAsyncWork(items) {
const count = items.length
const promises = []
for (var i = 0; i < count; i++) {
const myPromise = asyncCall(items[i])
promises.push(myPromise)
}
await Promise.all(promises)
}
async function doAsyncWork(items) {
const promises = items.map((item) => asyncCall(item))
await Promise.all(promises)
}
Если нужно выполнить Promise функции последовательно, а не конкурентно:
async function doAsyncWork(items) {
for (const item of items) {
await asyncCall(item)
}
}
редактировать статью
Источник — bxnotes
обновлено September 22, 2018
Атрибуты async, defer в JS
Вы здесь:
Главная — JavaScript — JavaScript Основы — Атрибуты async, defer в JS
Загрузка страницы в браузер
Когда HTML страница попадает в браузер, то сразу запускается процесс распознавания: сканируется код страницы сверху вниз, формируется DOM-дерево, загружаются CSS стили, шрифты. Тег за тегом на странице строится верстка. Когда в конце страницы браузер видит тег скрипта, то загружает код скрипта и начинает взаимодействовать с ним.
Ошибка новичков
В чем состоит типичная ошибка новичков? Они тег script помещают не в конец страницы, а между тегами head. Чего категорически нельзя делать. Что будет если DOM-дерево еще не успело построиться, а скрипт уже начал взаимодействовать с элементами, которых еще нет на странице? Это неизбежно приведет к ошибке и скрипт может заблокировать страницу из-за порочной цепочки: Скрипт ждет загрузки нужных ему элементов, а загрузка элементов встала на паузу из-за зависания скрипта. В итоге сайт зависает и становится не работоспособным.
<script src="script.js"></script>
Загрузка скрипта на страницу
Но не все так просто, даже при правильном размещении тега script, все равно может возникнуть проблема. Причина — объемная HTML структура на больших проектах. Пока загрузятся все теги и дойдет очередь до скрипта, пройдёт сколько-то времени. Тогда у пользователей с медленным интернетом, задержки будут значительными. Поэтому были придуманы два атрибута defer и async, которые помогут решить эту проблему.
Атрибут defer
Атрибут defer сообщает браузеру, что он должен продолжать обрабатывать страницу и запускать скрипт, только после готовности DOM-дерева. Скрипты с атрибутом defer никогда не заблокирует страницу. Пример ниже демонстрирует, что допустимо ставить тег скрипт выше элементов страницы, если указан атрибут defer.
<body>
<head>
<script defer src="script.js"></script>
</head>
<h4>Атрибут defer</h4>
<p>загружает по порядку расположения.</p>
</body>
Отложенные скрипты с помощью атрибута defer, сохраняют порядок относительно друг друга, как и скрипты без данного атрибута. В теории все ссылки на скрипты с тегом defer можно поместить в тег head, но в реальной жизни так все равно не нужно делать. Например сервис Google PageSpeed на такое будет ругаться и занижать оценку по скорости загрузки сайта.
Атрибут async
Асинхронные скрипты с атрибутом async, не ждут загрузки элементов страницы и не ждут загрузки друг друга. Они абсолютно независимые. Мы не знаем какой из двух скриптов загрузится первым и начнет работать. Чем полезно такое на первый взгляд непредсказуемое поведение скрипта? Дело в том, что иногда мы будем подключать различные сторонние скрипты, которые не особо привязаны к DOM-структуре. Например сторонним скриптам, таким как метрикам и счетчикам главное отловить, что пользователь зашел на сайт.
<body>
<head>
<script async src="script.js"></script>
<script async src="common.js"></script>
</head>
<h4>Атрибут async</h4>
<p>не зависит от DOM-структуры. </p>
</body>
Когда мы используем скрипты с атрибутом async, то мы должны быть абсолютно точно уверены, что этот скрипт не зависит от DOM-структуры. Для него совершенно не важно успела она сформироваться или нет. Кроме того он должен не зависеть от каких-то других скриптов и библиотек.
Итоги
Атрибуты defer и async добавляют гибкости при подключении внешних скриптов к HTML документу. Если мы применяем атрибут async к тегу script, то это означает следующее: Когда браузер дойдет до скрипта, то он не будет ждать пока скрипт загрузится целиком, а продолжит его загрузку в фоновом режиме и продолжит считывание файла дальше. Это может быть актуально например, если тег script подключен между тегами head. Обычно таким образом подключают к сайту счетчики и веб-аналитику.
<!-- Пример подключения Google Analytics -->
<script async src="https://google-analytics. com/analytics.js"></script>
Атрибут defer делает тоже самое, но с одним небольшим исключением. Если на странице есть еще один тег script с атрибутом defer, то браузер обещает нам, что даже несмотря на асинхронную загрузку данного скрипта в фоновом режиме, порядок загрузки скриптов и их выполнение сохранится. Первым загрузится скрипт, который выше расположен на странице, независимо от размера этого скрипта.
-
Создано 17.08.2020 10:27:24 -
Михаил Русаков
Предыдущая статья Следующая статья
Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!
Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk. com/rusakovmy.
Если Вы не хотите пропустить новые материалы на сайте,
то Вы можете подписаться на обновления: Подписаться на обновления
Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.
Порекомендуйте эту статью друзьям:
Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):
-
Кнопка:
<a href=»https://myrusakov.ru» target=»_blank»><img src=»https://myrusakov.ru/images/button.gif» alt=»Как создать свой сайт» /></a>Она выглядит вот так:
-
Текстовая ссылка:
<a href=»https://myrusakov.ru» target=»_blank»>Как создать свой сайт</a>Она выглядит вот так: Как создать свой сайт
- BB-код ссылки для форумов (например, можете поставить её в подписи):
[URL=»https://myrusakov. ru»]Как создать свой сайт[/URL]
C# async await Потоки ч.6
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp50
{
internal class Prog
{
public static void Method()
{
for (int index = 0; index < 80; ++index)
{
Thread.Sleep(100);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write(«-«);
}
}
[AsyncStateMachine(typeof(Prog.d__1))]
[DebuggerStepThrough]
public static void MethodAsync()
{
Prog.d__1 stateMachine = new Prog.d__1();
stateMachine.t__builder = AsyncVoidMethodBuilder.Create();
stateMachine.E1__state = -1;
stateMachine. t__builder.Start < Prog.d__1 > (ref stateMachine);
}
private static void Main(string[] args)
{
Prog.MethodAsync();
Console.WriteLine(«Main завершился»);
Console.ReadKey();
}
public Prog()
{
return;
}
[CompilerGenerated]
private sealed class d__1 : IAsyncStateMachine
{
public int E1__state;
public AsyncVoidMethodBuilder t__builder;
private Task E5__1;
private TaskAwaiter u__1;
public d__1()
{
return;
}
void IAsyncStateMachine.MoveNext()
{
int num1 = this.E1__state;
try
{
TaskAwaiter awaiter;
int num2;
if (num1 != 0)
{
// ISSUE: method pointer
this. E5__1 = new Task(new Action(Method));
this.E5__1.Start();
awaiter = this.E5__1.GetAwaiter();
if (!awaiter.IsCompleted)
{
this.E1__state = num2 = 0;
this.u__1 = awaiter;
Prog.d__1 stateMachine = this;
this.t__builder.AwaitUnsafeOnCompleted < TaskAwaiter, Prog.d__1 > (ref awaiter, ref stateMachine);
return;
}
}
else
{
awaiter = this.u__1;
this.u__1 = new TaskAwaiter();
this.E1__state = num2 = -1;
}
awaiter.GetResult();
}
catch (Exception ex)
{
this. E1__state = -2;
this.t__builder.SetException(ex);
return;
}
this.E1__state = -2;
this.t__builder.SetResult();
}
[DebuggerHidden]
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
}
}
}
}
JavaScript async и ожидание в циклах
1 мая 2019
Basic async
и await
прост. Все становится немного сложнее, когда вы пытаетесь использовать await
в циклах.
В этой статье я хочу поделиться некоторыми подводными камнями, на которые следует обратить внимание, если вы собираетесь использовать await
в циклах.
Прежде чем начать
Я предполагаю, что вы знаете, как использовать async
и await
. Если вы этого не сделаете, прочитайте предыдущую статью, чтобы ознакомиться, прежде чем продолжить.
Подготовка примера
Предположим, вы хотите получить количество фруктов в корзине с фруктами.
const fruitBasket = {
яблоко: 27,
виноград: 0,
груша: 14
}
Вы хотите получить количество фруктов из корзины с фруктами. Чтобы получить количество фруктов, вы можете использовать функцию getNumFruit
.
const getNumFruit = fruit => {
вернуть фруктыКорзина [фрукты]
}
const numApples = getNumFruit ('яблоко')
приставка.журнал (numApples) // 27
Теперь предположим, что fruitBasket
находится на удаленном сервере. Доступ к нему занимает одну секунду. Мы можем имитировать эту задержку в одну секунду с таймаутом. (Если у вас возникли проблемы с пониманием кода тайм-аута, обратитесь к предыдущей статье).
const sleep = ms => {
вернуть новое обещание (разрешение => setTimeout (разрешение, мс))
}
const getNumFruit = fruit => {
return sleep (1000) .then (v => fruitBasket [фрукты])
}
getNumFruit ('яблоко')
. then (num => console.log (num)) // 27
Наконец, допустим, вы хотите использовать await
и getNumFruit
, чтобы получить номер каждого фрукта в асинхронной функции.
const control = async _ => {
console.log ("Пуск")
const numApples = ожидание getNumFruit ('яблоко')
console.log (число яблок)
const numGrapes = ожидание getNumFruit ('виноград')
console.log (numGrapes)
const numPears = ожидание getNumFruit ('груша')
console.log (numPears)
приставка.журнал ('Конец')
}
С этим мы можем начать рассматривать await
в циклах.
Ожидание в цикле for
Допустим, у нас есть набор фруктов, которые мы хотим получить из корзины с фруктами.
const fruitToGet = ['яблоко', 'виноград', 'груша']
Мы собираемся просмотреть этот массив в цикле.
const forLoop = async _ => {
console.log ("Пуск")
for (let index = 0; index length; index ++) {
// Получаем количество каждого фрукта
}
приставка.журнал ('Конец')
}
В цикле for мы будем использовать getNumFruit
, чтобы получить номер каждого фрукта. Мы также введем номер в консоль.
Поскольку getNumFruit
возвращает обещание, мы можем ожидать
разрешенного значения перед его регистрацией.
const forLoop = async _ => {
console.log ("Пуск")
for (let index = 0; index
Когда вы используете await
, вы ожидаете, что JavaScript приостановит выполнение до тех пор, пока ожидаемое обещание не будет разрешено. Это означает, что await
с в цикле for должны выполняться последовательно.
Результат - то, чего вы ожидали.
«Старт»
"Яблоко: 27"
'Виноград: 0'
'Груша: 14'
'Конец'
Это поведение работает с большинством циклов (например, , тогда как
и for - из
циклов)…
Но он не работает с циклами, требующими обратного вызова. Примеры таких циклов, для которых требуется резерв, включают для каждого
, карту
, фильтр
и уменьшить
. Мы рассмотрим, как await
влияет на для каждой
, map
и filter
в следующих нескольких разделах.
Ожидание в цикле forEach
Проделаем то же самое, что и в примере цикла for. Во-первых, давайте пройдемся по множеству фруктов.
const forEachLoop = _ => {
приставка.журнал ('Старт')
fruitToGet.forEach (fruit => {
// Отправляем обещание для каждого фрукта
})
console.log ('Конец')
}
Далее попробуем получить количество фруктов с помощью getNumFruit
. (Обратите внимание на ключевое слово async
в функции обратного вызова. Нам нужно это ключевое слово async
, потому что await
находится в функции обратного вызова).
const forEachLoop = _ => {
console. log ("Пуск")
fruitToGet.forEach (async fruit => {
const numFruit = ждать getNumFruit (фрукт)
приставка.журнал (numFruit)
})
console.log ('Конец')
}
Вы могли ожидать, что консоль будет выглядеть так:
«Старт»
'27'
'0'
'14'
'Конец'
Но реальный результат другой. JavaScript продолжает вызывать console.log ('End')
до того, как будут разрешены обещания в цикле forEach.
Консоль ведет журналы в следующем порядке:
«Старт»
'Конец'
'27'
'0'
'14'
JavaScript делает это, потому что forEach
не поддерживает обещания.Он не может поддерживать async
и await
. Вы не можете использовать await
из для каждого
.
Ждать с картой
Если вы используете await
в map
, map
всегда будет возвращать массив обещаний. Это потому, что асинхронные функции всегда возвращают обещания.
const mapLoop = async _ => {
console.log ("Пуск")
const numFruits = await fruitToGet.map (async fruit => {
const numFruit = ждать getNumFruit (фрукт)
return numFruit
})
приставка.журнал (numFruits)
console.log ('Конец')
}
«Старт»
'[Обещание, обещание, обещание]'
'Конец'
Так как карта
всегда возвращает обещания (если вы используете await
), вам нужно дождаться разрешения массива обещаний. Вы можете сделать это с помощью await Promise.all (arrayOfPromises)
.
const mapLoop = async _ => {
console.log ("Пуск")
const promises = fruitToGet.map (async fruit => {
const numFruit = ждать getNumFruit (фрукт)
return numFruit
})
const numFruits = ожидание обещания.все (обещает)
console.log (numFruits)
console.log ('Конец')
}
Вот что вы получите:
«Старт»
'[27, 0, 14]'
'Конец'
Вы можете управлять значением, которое вы возвращаете в своих обещаниях, если хотите. Разрешенными значениями будут значения, которые вы вернете.
const mapLoop = async _ => {
// ...
const promises = fruitToGet.map (async fruit => {
const numFruit = ждать getNumFruit (фрукт)
// Добавляет несколько фруктов перед возвратом
return numFruit + 100
})
//...
}
«Старт»
'[127, 100, 114]'
'Конец'
Ожидание с фильтром
При использовании filter
вы хотите отфильтровать массив с определенным результатом. Допустим, вы хотите создать массив из более чем 20 фруктов.
Если вы обычно используете фильтр
(без ожидания), вы будете использовать его так:
// Фильтр, если нет ожидания
const filterLoop = _ => {
console.log ("Пуск")
const moreThan20 = fruitToGet.filter (fruit => {
const numFruit = fruitBasket [фрукты]
return numFruit> 20
})
console.log (более 20)
console.log ('Конец')
}
Ожидается, что на больше, чем на 20,
будет содержать только яблоки, потому что есть 27 яблок, но есть 0 винограда и 14 груш.
«Старт»
['яблоко']
'Конец'
await
в фильтре
работает иначе. Фактически, это вообще не работает. Вы получаете обратно нефильтрованный массив…
const filterLoop = async _ => {
приставка.журнал ('Старт')
const moreThan20 = await fruitToGet.filter (async fruit => {
const numFruit = ждать getNumFruit (фрукт)
return numFruit> 20
})
console.log (более 20)
console.log ('Конец')
}
«Старт»
['яблоко', 'виноград', 'груша']
'Конец'
Вот почему это происходит.
Когда вы используете await
в обратном вызове filter
, обратный вызов всегда возвращает обещание. Поскольку обещания всегда истинны, все элементы в массиве проходят фильтр.Запись await
в фильтре
аналогична написанию этого кода:
// Все проходит через фильтр ...
const filter = array.filter (() => true)
Чтобы правильно использовать await
и filter
, нужно выполнить три шага:
- Используйте
map
для возврата обещаний массива -
await
массив обещаний -
фильтр
разрешенные значения
const filterLoop = async _ => {
приставка.журнал ('Старт')
const promises = ждать фруктовToGet.map (fruit => getNumFruit (фрукты))
const numFruits = ждать Promise.all (обещания)
const moreThan20 = fruitToGet.filter ((fruit, index) => {
const numFruit = numFruits [индекс]
return numFruit> 20
})
console.log (более 20)
console.log ('Конец')
}
Старт
[ 'яблоко' ]
Конец
Ожидать со снижением
В этом случае предположим, что вы хотите узнать общее количество фруктов в корзине для фруктов.Обычно вы можете использовать reduce
, чтобы пройти по массиву и суммировать число.
// Уменьшить, если нет ожидания
const reduceLoop = _ => {
console.log ("Пуск")
const sum = fruitToGet.reduce ((sum, fruit) => {
const numFruit = fruitBasket [фрукты]
сумма возврата + numFruit
}, 0)
console.log (сумма)
console.log ('Конец')
}
Всего вы получите 41 фрукт. (27 + 0 + 14 = 41).
«Старт»
'41'
'Конец'
Когда вы используете await
с сокращением, результаты становятся очень беспорядочными.
// Уменьшаем, если ждем getNumFruit
const reduceLoop = async _ => {
console.log ("Пуск")
const sum = await fruitToGet.reduce (async (sum, fruit) => {
const numFruit = ждать getNumFruit (фрукт)
сумма возврата + numFruit
}, 0)
console.log (сумма)
console.log ('Конец')
}
«Старт»
'[обещание объекта] 14'
'Конец'
Что ?! [Обещание объекта] 14
?!
Расчленение этого i
Cơ bản về async await trong javascript
Nếu cho tôi 6 tiếng để n hạ một cái cây, tôi sẽ dành 4 tiếng đầu tiên để mài rìu.
- Авраам Линкольн
Bn có thể đọc bài gốc tại đây
Khi bắt đầu lập trình với nodejs, vì javascript (js) là bất đồng bộ (асинхорный) nên mình gặp khó khăn trong việc tổ chức code giống như trong lng trình (синхр. Việc cho các oạn code vào trong các callback khiến mình cảm этот код trở lên khó c theo luồng như trong PHP hay Ruby, nên mình đã tìm hiểu và sử dụng cúúa JaP as6. Sử dụng các cú pháp mới này giúp cho code của mình có thể tổ chức rõ ràng hơn.
Khi sử dụng cú pháp async ожидает, что bạn phải nắm được luồng chạy trong các hàm này và cái gì c trả về trong các hàm này. Sau đây mình xin trình bày trình tự chạy các câu lệnh khi có async await trong nodejs.
выполнить ()
function execute () {
findResult ()
console.log («конец выполнения»)
}
function findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
console.log ('до findResult')
db.collection ('больницы'). findOne ({имя: '医療 法人 神 甲 会 隈 病院'},
function (err, result) {
приставка.log ('внутренний обратный вызов findResult')
}
)
console.log ('после findResult')
}
Đoạn code trên có findOne () là hàm chạy async nên chẳng có gì bàn cãi khi thứ tự in ra sẽ là:
до findResult
после findResult
конец выполнения
внутренний обратный вызов findResult
выполнить ()
function execute () {
findResult ()
console.log («конец выполнения»)
}
асинхронная функция findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
приставка.log ('перед findResult')
var result = await db.collection ('больницы')
.findOne ({name: '医療 法人 神 甲 会 隈 病院'}) // здесь не пишите обратный вызов
// обрабатываем с результатом здесь
console.log ('после findResult')
}
Ngi ta tạo ra async await là tránh các hàm callback nên đừng viết await và callback cùng nhau.
Bạn dự đoán đoạn code trên sẽ in ra thứ tự thế nào?
Th tự sẽ như sau:
до findResult
конец выполнения
после findResult
Để trả lời câu hỏi trên thì cần nhớ một số chú ý sau:
await luôn luôn nằm trong hàm async như ví dụ trên (await không thể nằm trong hàm không được khai báo từ khóa async phía
Thứ tự thực hiện các câu lệnh trong js nói chung hay nodejs nói riêng đều là chạy từ trên xuống dưới (nghĩa là chạy sync chứ không phải as li Цикл событий tham khảo thêm ở bài viết trong js)
Khi gặp await , nó sẽ convert hàm ó thành promise với callback là tất cả những phần code phía sau await đó.Bản chất await là một Promise, phần code nằm sau await thực chất là code nằm trong callback của hàm await đó. Ví dụ 2 oạn mã dưới đây là tương ng nhau:
trên)
async function test () {
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после findResult:', результат)
// ... здесь еще код ...
}
// tương đương với
function test () {
db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'}, function (result) {
console.log ('после findResult:', результат)
//... больше кода здесь ...
})
}
Nếu nắm được ví dụ trên kia rồi thì những đoạn code phía sau đây bạn sẽ biết thứ tự và kết quả được in ra như thế nào:
VD1:
выполнить ()
function execute () {
var result = findResult ()
console.log (результат)
}
асинхронная функция findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
console.log ('до findResult')
await db.collection ('больницы'). findOne ({имя: '医療 法人 神 甲 会 隈 病院'})
console.log ('после findResult')
}
Th tự in ra là:
до findResult
Promise {}
после findResult
ù ».
Th để lấy kết quả thực từ câu lệnh findOne () của VD1, hàm execute () thì chúng ta cần phải làm gì? Vì findResult () trả về một Promise nên ta chỉ cần gọi hàm then () ở nơi được trả về là được, xét VD2 sau ây:
VD2:
выполнить ()
function execute () {
findResult (). then (function (result) {// вызов then () здесь, чтобы зафиксировать результат в асинхронной функции
console.log (результат)
})
console.log ('конец выполнения')
}
асинхронная функция findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
приставка.log ('перед findResult')
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после findResult')
вернуть результат
}
Что нужно сделать:
до findResult
конец выполнения
после findResult
{_id: 59e8d6930c9c77b21c42d704,
.....}
Gọi hàm có từ khóa async phía trước luôn trả về một Promise, dù trong hàm đó có await hay không.
VD1:
function test () {
var обещание = returnTen ()
приставка.журнал (обещание)
}
асинхронная функция returnTen () {
возврат 10
}
test () // Обещание {10}
VD на обещание trả về có kết quả là 10 luôn.
VD2:
function test () {
var обещание = returnTen ()
console.log (обещание)
}
асинхронная функция returnTen () {
вернуться ждать 10
}
test () // Обещание {<ожидание>}
VD này Promise trả về chưa có kết quả luôn.
Chú ý là nếu await nằm trong loop thì sẽ khác biệt một chút, xét đoạn code sau:
для (var i = 0; i <3; i ++) {
приставка.log ('перед async:', я)
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после async:', я)
}
Nhiều người có l sẽ nghĩ đoạn code trên tương đương với:
для (var i = 0; i <3; i ++) {
console.log ('перед async:', я)
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после async:', я)
}
// перед async: 0
// перед async: 1
// перед async: 2
// после async: 3
// после async: 3
// после async: 3
Nhưng không phải, mỗi khi gặp await thì phải đợi kết quả trả về mới chạy tiếp tới i tiếp theo, oạn code tương đương sẽ là như sau:
var i = 0
приставка.log ('before async:', i) // перед async: 0
db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'}, function () {
console.log ('after async:', i) // после async: 0
я ++
console.log ('before async:', i) // перед async: 1
db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'}, function () {
console.log ('after async:', i) // после async: 1
я ++
console.log ('before async:', i) // перед async: 2
db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'}, function () {
приставка.log ('after async:', i) // после async: 2
я ++
})
})
})
Ví dụ kiểm tra:
выполнить ()
function execute () {
findResult (). then (function (result) {// вызов then () здесь, чтобы зафиксировать результат в асинхронной функции
console.log (результат)
})
console.log ('конец выполнения')
}
асинхронная функция findResult () {
for (var i = 0; i <5; i ++) {
console.log ('перед findResult:', i)
result = await db.collection ('больницы'). findOne ({имя: '医療 法人 神 甲 会 隈 病院'})
приставка.журнал (я, результат)
}
вернуться я
}
Что нужно сделать перед поиском:
перед findResult: 0
конец выполнения
0 {_id: 59f972567909e65c67a28b1b,
..................}
перед findResult: 1
1 {_id: .....,
..................}
перед findResult: 2
2 {_id: .....,
.. ................}
перед findResult: 3
3 {_id: .....,
................ ..}
перед findResult: 4
4 {_id: .....,
..................}
5 // <= это результат консоли.log (результат) в обратном вызове в функции execute ()
VD1: Hàm thứ 2 là hàm bình thường nhưng có khối async ở phía trong.
выполнить ()
function execute () {
findResult (). then (function (result) {
console.log ('результат 1:', результат)
})
console.log ('конец выполнения')
}
асинхронная функция findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
fA (). then (function (результат) {
console.log ('результат 2:', результат)
})
console.log ('до findResult')
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после findResult')
вернуть результат
}
function fA () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
console.log ('до fA')
var result = db.collection ('больницы'). findOne ({name: '都 志 見 病院'})
console.log ('после fA')
вернуть результат
}
То в ra sẽ là:
до fA
после fA
до findResult
конец выполнения
результат 2: {...}
после findResult
результат 1: {...}
Цена:
Trình tự in ra từ đầu cho tới "конец выполнения" như dự đoán vì code chạy đúng như trình tự синхронно (đồng bộ, hay từ trên xuống dưới)
Vì sao " after findResult " lại được in ra trước " result 1: {...} " ???:
Vì khi gọi await ở trong hàm findResult thì console.log ( 'after findResult') ã bị đặt vào callback của hàm await đó rồi mới tới return result cho callback của result1 được in ra.Vì sao " результат 2: {...} " được in ra trước " result 1: {...} " ???:
2 lời gọi fA () trong findResult () в findResult () trong execute () là 2 hàm async không phụ thuộc vào nhau nên hàm nào có kết quả trả về trước sẽ c thực thi trước.
trên thì câu lệnh async ở dòng 36 có kết quả trả về nhanh hơn kết quả trả về ở câu lnh 22.
Nếu không tin bạn có thể tùy biến cho câu thingânh, lúc này " result2: {...} "sẽ được in ra sau" результат 1: {...} "
VD2: Hàm thứ 2 là hàm async
выполнить ()
function execute () {
findResult (). then (function (result) {
console.log ('результат 1:', результат)
})
console.log ('конец выполнения')
}
асинхронная функция findResult () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
fA (). then (function (результат) {
console.log ('результат 2:', результат)
})
console.log ('до findResult')
var result = await db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log ('после findResult')
вернуть результат
}
асинхронная функция fA () {
for (var i = 0; i <100000; i ++) {
var j = 100
}
console.log ('до fA')
result = await db.collection ('больницы'). findOne ({имя: '都 志 見 病院'})
console.log ('после fA')
вернуть результат
}
То в ra sẽ là:
до fA
до findResult
конец выполнения
после fA
результат 2: {...}
после findResult
результат 1: {...}
Cái này c giải thích giống ví dụ trên, và cũng giống như ví dụ trên result 2 được in ra trước result 1 vì hàm async của nó được trả về giá trị sớm hơn.
Chú ý khi sử dụng await trong vòng lặp như ã nói phía trên.
Khi gặp await thì những đoạn code phía sau có kết quả trả về mới thực hiện được nên nếu phần code phía sau không phụ thuộc vào await thì bau4 nên 9028
Место нахождения:
async function test () {
var result1 = ждать db.collection ('больницы'). findOne ({name: '医療 法人 神 甲 会 隈 病院'})
console.log (результат1)
var result2 = await db.collection ('больницы'). findOne ({name: 'abcxyz'})
console.log (результат2)
}
контрольная работа()
troạn code trên result1 có kết quả trả về thì hàm lấy result2 mới được chạy. Nhưng điều bạn muốn là cả 2 hàm lấy result1 và result2 песня phải chạy song song, bạn cần chuyn thành như sau:
async function test () {
var обещание1 = db.collection ('больницы'). findOne ({имя: '医療 法人 神 甲 会 隈 病院'})
вар обещание2 = дб.collection ('больницы'). findOne ({name: 'abcxyz'})
var result1 = ждать обещания1
console.log (результат1)
var result2 = ждать обещания2
console.log (результат2)
}
контрольная работа()
Nhìn 2 đoạn code có vẻ giống nhau nhưng khác nhau một trời một vực đấy. Bạn nên đọc bài cơ chế hoạt động của javascript để nắm được trình tự javascript chạy các câu lệnh như thế nào.
Ссылка на хоо:
https://stackoverflow.com/questions/43302584/why-doesnt-the-code-after-await-run-right-away-isnt-it-suppposed-to-be-non-blo
Асинхронное программирование с помощью JavaScript - обратные вызовы, обещания и асинхронные / ожидающие
Подпишитесь на YouTube
Введение в асинхронное программирование на JavaScript
В целом JavaScript выполняет код неблокирующим образом.Это означает, что код, для завершения которого требуется некоторое время (например, доступ к API, чтение содержимого из локальной файловой системы и т. Д.), Выполняется в фоновом режиме, и параллельно выполнение кода продолжается. Это поведение описывается термином «асинхронное программирование».
Поскольку JavaScript выполняется таким неблокирующим образом, вам необходимо принять дополнительные меры для работы с этим кодом, если вам нужно получить результат до того, как будет выполняться следующий код.
Если вам нравится CodingTheSmartWay, подумайте о поддержке нас через Patreon.С вашей помощью мы сможем чаще выпускать руководства для разработчиков. Большое спасибо!
Давайте посмотрим на следующий первый пример, чтобы лучше понять проблему:
const getTodo = () => {
setTimeout (() => {
return {текст: 'Полный пример кода'}
}, 2000)
}
const todo = getTodo ()
console.log (todo.text)
Здесь мы определяем функцию getTodo . Эта функция возвращает элемент задачи (как объект JSON со строковым свойством , текст ).Чтобы смоделировать вызов API, мы задерживаем ответ на 2 секунды, заключая оператор return в анонимную функцию, которая передается в setTimeout . Второй параметр setTimeout - это количество миллисекунд, на которое должно быть отложено выполнение функции.
Результат вызова функции getTodo сохраняется в константе с именем todo, и, наконец, мы пытаемся распечатать информацию text с помощью вызова console.log.Это приводит к следующей ошибке при выполнении этого кода:
Итак, почему мы получаем этот тип ошибки, в которой говорится, что он не может прочитать текст свойства из undefined. Причина очевидна: поскольку возвращаемое значение функции getTodo доставляется с задержкой в 2000 миллисекунд, объект недоступен при попытке вывести на консоль todo.text . Выполнение кода продолжилось, не дожидаясь завершения вызова getTodo .Это типичная проблема при выполнении асинхронного кода.
Так что же может быть решением этой проблемы? К счастью, у JavaScript есть много решений. Сначала мы рассмотрим обратные вызовы в следующем разделе.
Обратные вызовы
Обратные вызовы - это простые функции, которые используются для уведомления вызывающего экземпляра, когда асинхронный блок кода выполнен и доступен результат.