Skip to main content

promises

Промисы нужны для выполнения асинхронных операций. После выполнения которых можно использовать цепочку методов then(), что упрощает синтаксис и читаемость кода.

Промис имеет три стадии: pending - ожидание, resolve - удачное выпонение, reject - неудачное.

const somePromise = new Promise((resolve, reject) => {
// some async code
resolve(); // continue
reject(); // break
});

somePromise
.then((value) => {
// do something
})
.catch((error) => {
// do something
});

then - выполнить что-нибудь после удачного выполнения промиса. catch - обработчик ошибок.

then будет ждать успешного выполнения somePromise. Если ОК - то выполнитсья сам.

Можно составлять длинные цепочки из then. Промис возвращает промис, так как then видит только промисы. then также возвращает промис.

Промис c таймером resolve вызывается внутри самого себя.

function delay(ms) {
return new Promise(resolve => {
setTimeout(() => resolve('Done'), ms);
});
}

delay(1000).then(resolve => console.log(resolve));

Промис на событие resolve вызывается внутри тела функции промиса. На практике часто используют не сам промис, а функцию, которая его возвращает, чтобы передавать что-нибудь в параметре этой функции.

function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;

script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`Ошибка загрузки скрипта ${src}`));

document.head.append(script);
});
}

Вызов функции. В параметре передаем src.

let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");

promise.then(
script => alert(`${script.src} загружен!`),
error => alert(`Ошибка: ${error.message}`)
);

promise.then(script => alert('Ещё один обработчик...'));

Можно было записать короче:

loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js")
.then(
script => alert(`${script.src} загружен!`),
error => alert(`Ошибка: ${error.message}`)
)
.then(script => alert('Ещё один обработчик...'));

Множественные запросы

import httpClient from '../httpClient';
import { Dispatch } from '@reduxjs/toolkit';
import { IBrand, IRegion, IStorageplace } from 'interfaces';

const root = 'dictionaries/';

// dictionary-service
const dictionaryService = {
async getDictionary<T>(name: string): Promise<T[] | false> {
try {
const { data } = await httpClient.get(name);

return data;
} catch (e) {
return false;
}
},
};

...
// setDictionary - reducer redux-slice
setDictionary: (
state,
{ payload }: PayloadAction<{ name: keyof IDictionaryState; data: any }>,
) => {
return {
...state,
[payload.name]: payload.data,
};
},

...

// redux-thunk
export const getDictionaries = () => {
return async function (dispatch: Dispatch<any>) {
// множественные запросы
await Promise.all([
dictionaryService.getDictionary<IBrand>(`${root}brand`),
dictionaryService.getDictionary<IRegion>(`${root}region`),
dictionaryService.getDictionary<IStorageplace>(`${root}storageplace`),
]).then(data => {
// data - это массив ответов

// brand - Бренды
if (data[0]) {
dispatch(setDictionary({ name: 'brand', data: data[0] }));
} else {
dispatch(setMessage('Не удалось загрузить справочник "Бренды"'),);
}

// region - Регионы
if (data[1]) {
dispatch(setDictionary({ name: 'region', data: data[1] }));
} else {
dispatch(setMessage('Не удалось загрузить справочник "Регионы"'));
}

// storageplace - Склады
if (data[2]) {
dispatch(setDictionary({ name: 'storageplace', data: data[2] }));
} else {
dispatch(setMessage('Не удалось загрузить справочник "Склады"'));
}
});

dispatch(setLoadingAction(false));
};
};