Все async
функции возвращают обещание.Все они.
Это обещание в конечном итоге разрешится с любым значением, которое вы вернете из асинхронной функции.
await
блокирует только внутреннее выполнение функции async
.Он не блокирует ничего вне функции.Концептуально асинхронная функция начинает выполняться и, как только она достигает инструкции await
, она немедленно возвращает неисполненное обещание функции, и внешний мир выполнения получает это обещание и продолжает выполняться.
Некоторое время спустя, внутреннее обещание, которое было await
ed, выполнится, и тогда выполнение остальных внутренних функций будет продолжено.В конце концов внутренняя часть функции завершится и вернет значение.Это приведет к разрешению обещания, которое было возвращено функцией с этим возвращаемым значением.
К вашему сведению, в вашей load()
функции много лишних вещей.Вы можете изменить его следующим образом:
async function load() {
const data = await new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
console.log(`Data inside the function: ${JSON.stringify(data)}`);
return data;
}
на это:
function load() {
return new Promise(resolve => {
setTimeout(() => resolve([1, 2, 3]), 10);
}).then(data => data.map(i => i * 10));
}
Затем используйте его так:
load().then(result => {
console.log(result);
});
Или я предпочитаю инкапсулироватьручное создание обещания в их собственной функции, например:
function delay(t, v) {
return new Promise(resolve => {
setTimeout(resolve.bind(null, v), t);
});
}
function load() {
return delay(10, [1, 2, 3]).then(data => data.map(i => i * 10));
}
И, оказывается, эта небольшая функция delay()
обычно полезна во многих местах, где вы хотите отложить цепочку обещаний.
Спасибо всем, кто принял участие и дал мне понимание.Но я все еще не понимаю, как использовать await и async.
Во-первых, большую часть времени вы отмечаете только функцию async
, если вам нужно использовать await
внутри функции.
Во-вторых, вы чаще всего используете await
(из функции async
), когда у вас есть несколько асинхронных операций, и вы хотите их упорядочить - часто потому, что первая предоставляет результат, который используется в качестве входных данных для второй.Вы можете использовать await
, когда все, что у вас есть, это одна асинхронная операция, но она не дает большого преимущества перед простой .then()
.
Вот несколько примеров веских причин для использованияasync/await
:
Последовательность нескольких асинхронных операций
Представьте, что у вас есть getFromDatabase()
, getTheUrl()
и getTheContent()
, которые все асинхронны.В случае сбоя вы можете просто отклонить возвращенное обещание с первой ошибкой.
Вот как это выглядит без асинхронного / await:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
Вот как это выглядит с async/await
:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
В обоих случаях функция возвращает обещание, которое разрешается с помощью finalValue, поэтому вызывающая сторона использует эти две реализации одинаково:
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
Но вы заметите,что реализация async/await
имеет больше сериализованного синхронного вида и больше похожа на не асинхронный код.Многие считают, что это легче писать, легче читать и легче поддерживать.Чем больше обработки вы выполняете между этапами, включая переход, тем больше преимуществ дает версия async/await
.
Автоматический сбор отклоненных обещаний и синхронных исключений
Как яКак было сказано ранее, функции async
всегда возвращают обещание.Они также должны иметь встроенную обработку ошибок, которая автоматически передает ошибки обратно в это возвращенное обещание.
Само собой разумеется, что если вы вручную вернете обещание из функции async
и это обещание будет отклонено, то обещание, возвращенное из функции async
, будет отклонено.
Но также,если вы используете await
и любое обещание, которое вы ожидаете, отклоняется, и у вас нет .catch()
на обещании и у вас нет try/catch
вокруг него, то обещание, которое возвращает функция, будет автоматически отклонено.Итак, вернемся к нашему предыдущему примеру этого:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
Если какое-либо из трех обещаний, которые await
редактируются, отклоняется, то функция будет коротко замкнута (перестанет выполнять больше кода в функции)и отклонить асинхронное возвращенное обещание.Таким образом, вы получаете эту форму обработки ошибок бесплатно.
Затем, наконец, функция async
также отлавливает для вас синхронные исключения и превращает их в отклоненное обещание.
В обычной функции, которая возвращает обещание, такое как мы имели ранее:
function run() {
return getFromDatabase(someArg).then(key => {
return getTheURL(key);
}).then(url => {
return getTheContent(url);
}).then(content => {
// some final processing
return finalValue;
});
}
Если getFromDatabase()
выдает синхронное исключение (возможно, вызванное тем, что someArg
недопустимо), то эта общая функция run()
будет генерировать синхронно.Это означает, что для того, чтобы вызывающий мог перехватить все возможные ошибки из run()
, он должен оба окружить его try/catch
, чтобы перехватить синхронные исключения, и использовать .catch()
, чтобы перехватить отклоненное обещание:
try {
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
} catch(e) {
console.log(err);
}
Это грязно и немного повторяется.Но когда run()
объявлено async
, оно НИКОГДА не будет генерировать синхронно, потому что любое синхронное исключение автоматически преобразуется в отклоненное обещание, поэтому вы можете быть уверены, что фиксируете все возможные ошибки, когда оно написано таким образом:
async function run(someArg) {
let key = await getFromDatabase(someArg);
let url = await getTheURL(key);
let content = await getTheContent(url);
// some final processing
return finalValue;
}
// will catch all possible errors from run()
run(someArg).then(finalValue => {
console.log(finalValue);
}).catch(err => {
console.log(err);
});
Должен ли я всегда вызывать все свои функции с ожиданием?
Во-первых, вы бы когда-либо использовали await
только с функцией, которая возвращает обещание как await
не дает никакой пользы, если функция не возвращает обещание (просто добавляя в беспорядок ваш код, если не нужно).
Во-вторых, используете ли вы await
или нет, зависит от контекста обеих вызывающих функций(поскольку вы ДОЛЖНЫ быть в функции async
, чтобы использовать await
и в потоке логики, и будет ли полезно использовать await
или нет.
Места, где использовать бессмысленноawait
async function getKey(someArg) {
let key = await getFromDatabase(someArg);
return key;
}
await
здесь не делает ничего полезного. Вы не выполняете последовательность нескольких асинхронных операций и не выполняете никакой обработкина возвращаемое значение.Вы можете выполнить точно такой же код, просто вернув обещание напрямую:
async function getKey(someArg) {
return getFromDatabase(someArg);
}
И, если вы знаете, что getFromDatabase()
никогда не генерирует синхронно, вы даже можете удалить async
из объявления:
function getKey(someArg) {
return getFromDatabase(someArg);
}
Допустим, я пишу код, состоящий из нескольких функций в нескольких файлах.Если я в конечном итоге использую библиотеку, которая возвращает Promise или это асинхронная функция, я должен проследить все мои вызовы функций из асинхронной точки до точки входа приложения и добавить ожидание перед всеми вызовами функций после их асинхронного выполнения?
Это слишком общий вопрос, на который сложно ответить в общем случае.Вот некоторые мысли в этом общем направлении:
Как только любая часть вашего результата, которую вы пытаетесь вернуть из своей функции A()
, становится асинхронной или использует любую асинхронную операцию для получения,сама функция асинхронная.В простом Javascript вы никогда не сможете синхронно вернуть асинхронный результат, поэтому ваша функция должна использовать асинхронный метод для возврата результата (обещание, обратный вызов, событие и т. Д.).
Любойфункция B()
, которая вызывает вашу асинхронную функцию A()
, которая также пытается вернуть результат на основе того, что она получает от A()
, теперь также асинхронна и также должна сообщать свой результат обратно, используя асинхронный механизм.Это верно для функции C()
, которая вызывает B()
и должна сообщать вызывающему ее результат.Итак, вы можете сказать, что асинхронное поведение заразительно.Пока вы не достигнете некоторой точки в цепочке вызовов, где вам больше не нужно сообщать результат, все должно использовать асинхронные механизмы для передачи результата, ошибки и завершения.
Нет особой необходимости отмечать функцию async
, если вам не требуется одно из преимуществ функции async
, например, возможность использовать await
внутри этой функции или автоматическая обработка ошибок, которую она обеспечивает.,Вы можете написать функции, которые возвращают обещания, просто без использования async
в объявлении функции.Итак, «НЕТ», я не возвращаюсь по цепочке вызовов, делая все async
.Я делаю функцию асинхронной только в том случае, если для этого есть особая причина.Обычно причина в том, что я хочу использовать await
внутри функции, но есть также автоматический перехват синхронных исключений, которые превращаются в отклонения обещаний, которые я описал ранее.Обычно это не требуется для кода с хорошим поведением, но иногда это полезно для кода с плохим поведением или кода с неопределенным поведением.
await
также используется только при наличии определенной причиныдля этого.Я не просто автоматически использую его для каждой функции, которая возвращает обещание.Я описал выше причины использовать его.Можно по-прежнему использовать .then()
просто отлично для обработки результата от одного вызова функции, который возвращает обещание.В некоторых случаях это просто вопрос личного стиля, хотите ли вы использовать .then()
или await
, и нет особой причины, по которой это должно быть так или иначе.
Или, может быть, я должен просто привыкнуть вызывать все мои функции с ожиданием, независимо от того, являются они асинхронными или нет?
Абсолютно НЕТ!Во-первых, последнее, что вы хотите сделать, - это взять совершенно синхронный код и излишне сделать его асинхронным или даже сделать его асинхронным.Асинхронный код (даже с async
и await
) сложнее писать, отлаживать, понимать и поддерживать, чем синхронный код, поэтому вы никогда не захотите без необходимости превращать синхронный код в асинхронный код, добавляя в него async/await
:
Например, вы никогда бы не сделали это:
async function random(min, max) {
let r = await Math.random();
return Math.floor((r * (max - min)) + min);
}
Во-первых, это совершенно синхронная операция, которая может быть закодирована следующим образом:
function random(min, max) {
let r = Math.random();
return Math.floor((r * (max - min)) + min);
}
Второй выключен,эта первая реализация async
усложнила использование функции, поскольку теперь она имеет асинхронный результат:
random(1,10).then(r => {
console.log(r);
});
Вместо простого синхронного использования:
console.log(random(1,10));