GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing developer.mozilla.org — Использование промисов - JavaScript | MDN This page was translated from English by the community. Learn more and join the MDN Web Docs community.
Использование промисов
В сущности, промис - это возвращаемый объект, в который вы записываете два колбэка вместо того, чтобы передать их функции. Например, вместо старомодной функции, которая принимает два колбэка и вызывает один из них в зависимости от успешного или неудачного завершения операции: js …современные функции возвращают промис, в который вы записываете ваши колбэки: js …или просто: js Мы называем это асинхронным вызовом функции. У этого соглашения есть несколько преимуществ. Давайте рассмотрим их. ГарантииВ отличие от старомодных переданных колбэков промис даёт некоторые гарантии:
Но наиболее непосредственная польза от промисов - цепочка вызовов (chaining). Цепочка вызововОбщая нужда - выполнять две или более асинхронных операции одна за другой, причём каждая следующая начинается при успешном завершении предыдущей и использует результат её выполнения. Мы реализуем это, создавая цепочку вызовов промисов (promise chain). Вот в чём магия: функция js или js Второй промис представляет завершение не только По сути, каждый вызванный промис означает успешное завершение предыдущих шагов в цепочке. Раньше выполнение нескольких асинхронных операций друг за другом приводило к классической "Вавилонской башне" колбэков: js В современных функциях мы записываем колбэки в возвращаемые промисы - формируем цепочку промисов: js Аргументы js Важно: Всегда возвращайте промисы в return, иначе колбэки не будут сцеплены и ошибки могут быть не пойманы (стрелочные функции неявно возвращают результат, если скобки {} вокруг тела функции опущены). Цепочка вызовов после catchМожно продолжить цепочку вызовов после ошибки, т. е. после new Promise((resolve, reject) => {
console.log('Начало');
resolve();
})
.then(() => {
throw new Error('Где-то произошла ошибка');
console.log('Выведи это');
})
.catch(() => {
console.log('Выведи то');
})
.then(() => {
console.log('Выведи это, несмотря ни на что');
});
В результате выведется данный текст: Начало Выведи то Выведи это, несмотря ни на что Заметьте, что текст "Выведи это" не вывелся, потому что "Где-то произошла ошибка" привела к отказу Распространение ошибкиВы могли ранее заметить, что doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Итоговый результат: ${finalResult}`))
.catch(failureCallback);
В основном, цепочка промисов останавливает выполнение кода, если где-либо произошла ошибка, и вместо этого ищет далее по цепочке обработчики ошибок. Это очень похоже на то, как работает синхронный код: try {
let result = syncDoSomething();
let newResult = syncDoSomethingElse(result);
let finalResult = syncDoThirdThing(newResult);
console.log(`Итоговый результат: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
Эта симметрия с синхронным кодом лучше всего показывает себя в синтаксическом сахаре async function foo() {
try {
let result = await doSomething();
let newResult = await doSomethingElse(result);
let finalResult = await doThirdThing(newResult);
console.log(`Итоговый результат: ${finalResult}`);
} catch(error) {
failureCallback(error);
}
}
Работа данного кода основана на промисах. Для примера здесь используется функция Промисы решают основную проблему пирамид, обработку всех ошибок, даже вызовов исключений и программных ошибок. Это основа для функционального построения асинхронных операций. Создание промиса вокруг старого колбэка
В идеале, все асинхронные функции уже должны возвращать промис. Но увы, некоторые APIs до сих пор ожидают успешного или неудачного колбэка переданных по старинке. Типичный пример: setTimeout(() => saySomething("10 seconds passed"), 10000);
Смешивание старого колбэк-стиля и промисов проблематично. В случае неудачного завершения К счастью мы можем обернуть функцию в промис. Хороший тон оборачивать проблематичные функции на самом низком возможном уровне, и больше никогда их не вызывать напрямую: const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
wait(10000).then(() => saySomething("10 seconds")).catch(failureCallback);
В сущности, конструктор промиса становится исполнителем функции, который позволяет нам резолвить или режектить промис вручную. Так как Композиция
Последовательное выполнение композиции возможно при помощи хитрости JavaScript: [func1, func2].reduce((p, f) => p.then(f), Promise.resolve()); Фактически, мы превращаем массив асинхронных функций в цепочку промисов равносильно: Это также можно сделать, объединив композицию в функцию, в функциональном стиле программирования: const applyAsync = (acc,val) => acc.then(val); const composeAsync = (...funcs) => x => funcs.reduce(applyAsync, Promise.resolve(x));
const transformData = composeAsync(func1, asyncFunc1, asyncFunc2, func2); transformData(data); В ECMAScript 2017, последовательные композиции могут быть выполнены более простым способом с помощью async/await: for (const f of [func1, func2]) {
await f();
}
Порядок выполненияЧтобы избежать сюрпризов, функции, переданные в Promise.resolve().then(() => console.log(2)); console.log(1); // 1, 2 Вместо немедленного выполнения, переданная функция встанет в очередь микрозадач, а значит выполнится, когда очередь будет пустой в конце текущего вызова JavaScript цикла событий (event loop), т.е. очень скоро: const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); wait().then(() => console.log(4)); Promise.resolve().then(() => console.log(2)).then(() => console.log(3)); console.log(1); // 1, 2, 3, 4 ВложенностьПростые цепочки promise лучше оставлять без вложений, так как вложенность может быть результатом небрежной структуры. Смотрите распространённые ошибки. Вложенность - это управляющая структура, ограничивающая область действия операторов catch. В частности, вложенный catch только перехватывает сбои в своей области и ниже, а не ошибки выше в цепочке за пределами вложенной области. При правильном использовании это даёт большую точность в извлечение ошибок: doSomethingCritical()
.then(result => doSomethingOptional()
.then(optionalResult => doSomethingExtraNice(optionalResult))
.catch(e => {})) // Игнорируется если необязательные параметр не выкинул исключение
.then(() => moreCriticalStuff())
.catch(e => console.log("Критическая ошибка: " + e.message));
Обратите внимание, что необязательный шаги здесь выделены отступом. Внутренний оператор catch нейтрализует и перехватывает ошибки только от doSomethingOptional() и doSomethingExtraNice(), после чего код возобновляется с помощью moreCriticalStuff(). Важно, что в случае сбоя doSomethingCritical() его ошибка перехватывается только последним (внешним) catch. Частые ошибкиВ этом разделе собраны частые ошибки, возникающие при создании цепочек промисов. Несколько таких ошибок можно увидеть в следующем примере: // Плохой пример! Три ошибки!
doSomething().then(function(result) {
doSomethingElse(result) // Забыл вернуть промис из внутренней цепочки + неуместное влаживание
.then(newResult => doThirdThing(newResult));
}).then(() => doFourthThing());
// Забыл закончить цепочку методом catch
Первая ошибка это неправильно сцепить вещи между собой. Такое происходит когда мы создаём промис но забываем вернуть его. Как следствие, цепочка сломана, но правильнее было бы сказать что теперь у нас есть две независимые цепочки, соревнующиеся за право разрешится первой. Это означает, что Вторая ошибка это излишняя вложенность, включая первую ошибку. Вложенность также ограничивает область видимости внутренних обработчиков ошибок, если это не то чего хотел разработчик, это может привести к необработанным ошибкам. Примером этого является пример как не нужно создавать промисы, который комбинирует вложенность с чрезмерным использованием конструктора промисов для оборачивания кода который уже использует промисы. Третья ошибка это забыть закончить цепочку ключевым словом Хорошим примером является всегда либо возвращать либо заканчивать цепочки промисов, и как только вы получаете новый промис, возвращайте его сразу же, чтобы не усложнять код излишней вложенностью: doSomething()
.then(function(result) {
return doSomethingElse(result);
})
.then(newResult => doThirdThing(newResult))
.then(() => doFourthThing())
.catch(error => console.log(error));
Обратите внимание что Теперь у нас имеется единственная определённая цепочка с правильной обработкой ошибок. Использование Смотрите также |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |