Теорія
Власні async
функції можуть бути ідентифіковані при перетворенні в рядки :
asyncFn[Symbol.toStringTag] === 'AsyncFunction'
Або за AsyncFunction
конструктором:
const AsyncFunction = (async () => {}).constructor;
asyncFn instanceof AsyncFunction === true
Це не буде працювати з вихідними даними Babel / TypeScript, оскільки asyncFn
це звичайна функція у перекладеному коді, це екземпляр Function
чи GeneratorFunction
ні AsyncFunction
. Щоб переконатися, що це не дасть помилкових спрацьовувань для генератора та звичайних функцій у перекладеному коді:
const AsyncFunction = (async () => {}).constructor;
const GeneratorFunction = (function* () => {}).constructor;
(asyncFn instanceof AsyncFunction && AsyncFunction !== Function && AsyncFunction !== GeneratorFunction) === true
Оскільки власні async
функції були офіційно представлені в Node.js в 2017 році, питання, ймовірно, стосується реалізації async
функції Babel , яка покладається на transform-async-to-generator
транпіляцію async
до функцій генератора, а також може використовуватись transform-regenerator
для транпіляції генератора до звичайних функцій.
Результатом async
виклику функції є обіцянка. Відповідно до пропозиції , обіцянка чи не обіцянка може бути передана await
, тому await callback()
є універсальною.
Є лише кілька випадків, коли це може знадобитися. Наприклад, власні async
функції використовують власні обіцянки внутрішньо і не піднімають глобальні, Promise
якщо їх реалізація була змінена:
let NativePromise = Promise;
Promise = CustomPromiseImplementation;
Promise.resolve() instanceof Promise === true
(async () => {})() instanceof Promise === false;
(async () => {})() instanceof NativePromise === true;
Це може вплинути на поведінку функції (це відома проблема реалізації обіцянок Angular та Zone.js ). Навіть тоді переважно виявити, що значення функції, що повертається, не є очікуваним, Promise
замість того, щоб виявити, що функцією є async
, оскільки ця сама проблема застосовна до будь-якої функції, яка використовує альтернативну реалізацію обіцянки, а не тільки async
( рішення вказаної проблеми Angular полягає в обгортанні async
return значення з Promise.resolve
).
Практика
Зовні async
функція - це просто функція, яка безумовно повертає рідну обіцянку, тому до неї слід ставитись як до такої. Навіть якщо функція колись була визначенаasync
, вона в якийсь момент може бути перетворена і стати регулярною функцією.
Функція, яка може повернути обіцянку
У ES6 функція, яка потенційно повертає обіцянку, може використовуватися з Promise.resolve
(допускає синхронні помилки) або обгорнутим Promise
конструктором (обробляє синхронні помилки):
Promise.resolve(fnThatPossiblyReturnsAPromise())
.then(result => ...);
new Promise(resolve => resolve(fnThatPossiblyReturnsAPromiseOrThrows()))
.then(result => ...);
У ES2017 це зроблено за допомогою await
(так слід писати приклад із запитання):
let result = await fnThatPossiblyReturnsAPromiseOrThrows();
...
Функція, яка повинна повертати обіцянку
Перевірка того, чи є об’єкт обіцянкою, - це питання окремого питання , але, як правило, воно не повинно бути занадто суворим або вільним, щоб охопити кутові справи. instanceof Promise
може не працювати, якщо глобальний Promise
був замінений, Promise !== (async () => {})().constructor
. Це може статися, коли Angular та non-Angular програми взаємодіють.
async
Спершу потрібно викликати функцію, яка вимагає бути , тобто завжди повертати обіцянку, потім повертається значення перевіряється як обіцянку:
let promise = fnThatShouldReturnAPromise();
if (promise && typeof promise.then === 'function' && promise[Symbol.toStringTag] === 'Promise') {
} else {
throw new Error('async function expected');
}
TL; DR: async
функції не слід відрізняти від звичайних функцій, які повертають обіцянки. Не існує надійного способу та немає практичних причин для виявлення неприродних перекладених async
функцій.
await
не обіцяєте, це все одно автоматично оберне)