Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает await с непромисами?
await работает не только с Promise! Это один из самых неожиданных аспектов async/await в JavaScript. Когда вы используете await с непромисом, JavaScript автоматически преобразует значение.
Базовое поведение
// await ждёт Promise
const result1 = await Promise.resolve(5);
console.log(result1); // 5
// await с обычным значением просто возвращает его
const result2 = await 5;
console.log(result2); // 5 (не Promise, не ошибка, просто 5)
// await с массивом
const result3 = await [1, 2, 3];
console.log(result3); // [1, 2, 3]
// await со строкой
const result4 = await 'hello';
console.log(result4); // "hello"
Это возможно благодаря Protocol Promise — JavaScript преобразует непромисы в Promise.
Как работает преобразование
Когда вы используете await с непромисом, JavaScript:
- Проверяет, является ли значение Promise
- Если да — ждёт его
- Если нет — оборачивает его в
Promise.resolve(value)
// Эти два эквивалентны:
const a = await 5;
const b = await Promise.resolve(5);
// Обе дают результат: 5
Более сложный пример
async function example() {
// await с непромисом
const num = await 42;
console.log(num); // 42
// await с объектом
const obj = await { name: 'John', age: 30 };
console.log(obj); // { name: 'John', age: 30 }
// await с функцией (возвращает функцию!)
const func = await (() => console.log('Hi'));
func(); // "Hi"
// await с null
const n = await null;
console.log(n); // null
}
example();
Thenable объекты
Thenable — это объект с методом .then(), похожий на Promise, но не совсем Promise.
const thenable = {
then(resolve, reject) {
resolve('Из thenable!');
}
};
async function test() {
const result = await thenable; // await работает!
console.log(result); // "Из thenable!"
}
test();
JavaScript игнорирует, является ли это реальным Promise. Если у объекта есть .then() — он работает с await.
Практический пример с thenable
class CustomAsync {
constructor(value) {
this.value = value;
}
then(resolve) {
console.log('then() вызван');
setTimeout(() => resolve(this.value), 1000);
}
}
async function main() {
const result = await new CustomAsync('Привет!');
console.log(result); // "Привет!" (через 1 секунду)
}
main();
await с классом, имеющим then
class MyClass {
constructor(value) {
this.value = value;
}
then(onFulfilled, onRejected) {
// Это делает класс совместимым с await
onFulfilled(this.value * 2);
}
}
async function example() {
const obj = new MyClass(21);
const result = await obj; // instanceof MyClass === true, но работает!
console.log(result); // 42
}
example();
Цепочка await с непромисами
async function chain() {
const a = await 1;
console.log(a); // 1
const b = await a + 1; // await 2
console.log(b); // 2
const c = await Promise.resolve(b + 1);
console.log(c); // 3
}
chain();
Ошибки и исключения
async function example() {
// await с непромисом — не может быть отклонено
const a = await 5; // всегда успешно
// await с отклоненным Promise
try {
const b = await Promise.reject('Ошибка!');
} catch (err) {
console.log(err); // "Ошибка!"
}
}
example();
Производительность: синхронное vs асинхронное
// Синхронный вызов быстрее
function sync() {
return 5;
}
function asyncWrap() {
return Promise.resolve(5);
}
async function test1() {
const a = await sync(); // Преобразуется в Promise.resolve(5)
}
async function test2() {
const b = await asyncWrap(); // Уже Promise
}
// test1 имеет overhead преобразования, но разница минимальна (микросекунды)
Интересный пример: автоматическое преобразование
async function example() {
// Это не ошибка, это работает!
const user = await {
id: 1,
name: 'John',
sayHi() { return 'Hi'; }
};
console.log(user.name); // "John"
console.log(user.sayHi()); // "Hi"
}
example();
Array как thenable
// Массивы не имеют .then(), поэтому это не работает как thenable
const arr = [1, 2, 3];
async function test() {
const result = await arr; // Просто вернёт массив
console.log(result); // [1, 2, 3] (не выполняет элементы)
}
test();
Проблема: забытый return вместо await
async function wrong() {
return fetchData(); // Ошибка! Возвращает Promise
}
async function correct() {
return await fetchData(); // Правильно (хотя await здесь не нужен в return)
}
// На самом деле в return await не нужен:
async function bestPractice() {
return fetchData(); // Просто верни Promise
}
Когда это полезно?
-
Теоретическое понимание: Показывает, как JavaScript работает под капотом
-
Кастомные async-like объекты:
class Delayed {
constructor(value, ms) {
this.value = value;
this.ms = ms;
}
then(resolve) {
setTimeout(() => resolve(this.value), this.ms);
}
}
await new Delayed('Success!', 1000); // Работает как Promise!
- Обратная совместимость: Старые API, которые возвращают thenable
Спецификация: Promise Resolution
В спецификации ECMAScript это называется Promise Resolution Procedure:
await x →
1. Если x это Promise → ждём его
2. Если x это thenable (имеет .then) → используем как Promise
3. Если x это значение → return Promise.resolve(x)
Итоговая таблица
Что передаём await | Результат |
|---|---|
| Promise<T> | T (ждём результат) |
| Thenable | Результат .then() |
| Число/строка/null | Само значение |
| Объект | Само значение |
| Функция | Сама функция |
| undefined | undefined |
Практический совет
// ❌ Плохо: ненужный await на непромисе
const value = await 5;
// ✅ Хорошо: просто используй значение
const value = 5;
// ✅ Правильно: await только на Promises
const data = await fetchData();
Понимание того, как await работает с непромисами, помогает глубже понять асинхронность в JavaScript и написать более предсказуемый код.