async

Асинхронный JavaScript: как работают Колбеки, промисы и асинхронное ожидание

Асинхронный JavaScript: как работают колбеки, промисы и async-await

Наши соц. сети: instagram, fb, tg

JavaScript позиционирует асинхронное программирование как фичу. Это означает, что если какое-либо действие занимает некоторое время, ваша программа может продолжать выполнять другие действия, пока предыдущее действие завершается. Как только это действие выполнено, ты можешь что-то сделать с результатом. Это будет отличным решением для таких функций, как выборка данных, но это может сбить с толку новичков. В JavaScript у нас есть несколько различных способов справиться с асинхронностью: колбеки, промисы и async-await.

Колбек функции

Функция колбек - это предоставляемая вами функция, которая будет выполняться после завершения асинхронной операции. Давай создадим фейковую функцию для получения пользовательских данных и используем колбек, чтобы что-то сделать с результатом.

Фейковая функция получения данных

Сначала мы создаем фейковуя функция получения данных, которая не принимает колбек. Поскольку

fakeData не существует в течение 300 миллисекунд, у нас нет синхронного доступа к ней.

const fetchData = userId => {

setTimeout(() => {

const fakeData = {

id: userId,

name: 'George',

};

// Our data fetch resolves

// After 300ms. Now what?

}, 300);

};


Чтобы действительно иметь возможность что-то делать с нашими fakeData, мы можем передать
fetchData как ссылку на функцию, которая будет обрабатывать наши данные!

const fetchData = (userId, callback) => {

setTimeout(() => {

const fakeData = {

id: userId,

name: 'George',

};

callback(fakeData);

}, 300);

};


Давайт создадим базовую функцию колбек и протестируем ее:


const cb = data => {

console.log("Here's your data:", data);

};

fetchData(5, cb);


Через 300 мс мы увидим следующее:


Here's your data: {id: 5, name: "George"}


Промисы

Объект Promise представляет возможное завершение операции в JavaScript. Промисы могут быть либо resolve, либо reject (решены или отклонены). Когда Promise разрешается (resolve()), ты можешь обработать его возвращенное значение с помощью метода then. Если Promise отклонено (reject()), ты можешь использовать catch для отлова ошибки и обработать ее.
Синтаксис объекта Promise следующий:

new Promise(fn);


Если fn - это функция, которая принимает функцию resolve и, необязательно, функцию reject.

fn = (resolve, reject) => {};


Функция получения данных (с промисами)

Давай использовать ту же функцию получения данных, что и раньше. Вместо того, чтобы передавать колбек, мы собираемся вернуть новый объект Promise, который разрешится (resolve()) с данными нашего пользователя через 300 мс. В качестве бонуса мы также можем дать ему небольшой шанс отказаться (reject()).

const fetchData = userId => {

return new Promise((resolve, reject) => {

setTimeout(() => {

if (Math.random() < 0.1) {

reject('Fetch failed!');

}

const fakeData = {

id: userId,

name: 'George',

};

resolve(fakeData);

}, 300);

});

};


Наша новая функция fetchData может использоваться следующим образом:

fetchData(5)

.then(user => {

console.log("Here's your data:", user);

})

.catch(err => {

console.error(err);

});


Если fetchData успешно разрешается (resolve()) (это будет происходить 90% времени), мы будем регистрировать наши пользовательские данные, как мы это делали с колбеком. Если он будет отклонен (reject), мы получим console.error сообщение об ошибке, которое мы создали (“Fetch failed!“)

Одна из плюшек Промисов - это то, что вы можете создать цепочку промисов, чтобы выполнить последующие промисы. Например, мы могли бы сделать что-то вроде этого:


fetchData(5)

.then(user => {

return someOtherPromise(user);

})

.then(data => {

console.log(data);

})

.catch(err => {

console.error(err);

});


Кроме того, мы можем передать массив промисов в Promise.all, чтобы принять меры только после того, как все промисы разрешены (resolve()),то есть все данные получены:

Promise.all([fetchData(5), fetchData(10)])

.then(users => {

console.log("Here's your data:", users);

})

.catch(err => {

console.error(err);

});


В этом случае, если оба промиса успешно разрешены (resolve()), будет выведено следующее:

Here's your data:

[{ id: 5, name: "George" }, { id: 10, name: "George" }]


async-await

Async-await предлагает другой синтаксис для написания промисов, которые некоторые считают более понятным. С помощью async-await ты можешь создать асинхронную функцию. Внутри этой асинхронной функции ты можешь дождаться результата Promise перед выполнением следующего кода! Давай посмотрим на наш пример получения данных.

const fetchUser = async userId => {

const user = await fetchData(userId);

console.log("Here's your data:", user);

};

fetchUser(5);


Не плохо, правда? Один маленький ньюансик: мы не рассматриваем наш случай отказа от Promise. Мы можем сделать это с помощью try / catch.

const fetchUser = async userId => {

try {

const user = await fetchData(userId);

console.log("Here's your data:", user);

} catch (err) {

console.error(err);

}

};

fetchUser(5);