Здравствуйте! Прошу подсказать по теории… Мне не совсем понятно, в чём отличие между .then и catch. До прочтения данной темы здесь, мне казалось, что у then и catch разные задачи. then призван выполнить что-то после выполнения promise. А catch отслеживает ошибки, которые могут проявиться в then. … В этой же статье, я увидел, что можно использовать либо then либо catch, цитирую
catch
Если мы хотели бы только обработать ошибку, то можно использовать null в качестве первого аргумента: .then(null, errorHandlingFunction). Или можно воспользоваться методом .catch(errorHandlingFunction), который сделает тоже самое:
Это меня очень сильно запутало. Пожалуйста, помогите разобраться
Далее… некоторые функции, находятся внутри Promise, а некоторые снаружи. Мне не понятно, как определить, оборачивать промисом функцию или писать её внутри? Прошу описать детальнее, что такое промис(как и то, как его вызывать). Насколько я понял, промисы необходимы для того, чтобы после выполнения, к примеру, запроса на сервер, мы могли бы с полученным ответом, что-то сделать, передав его в then. Также, промисы решают организационный вопрос, избавляя нас от цепочек колбэка и являются асинхронной функцией
Рассмотрим пример.Предположим, что нам нужно реализовать две функции, одна должна загружать файл с сервера, а другая открыть данный файл. Понятно из задачи, что они должны идти синхронно. Сначала, должна выполниться первая и только после этого, запускаться вторая. Ниже, я приведу пример одной задачи и в комментариях задам некоторые уточняющие вопросы. Прошу на них ответить.
Промис - это объект который дает тебе описать как работать с данными которые еще не появились программе.
Например у тебя есть магазин. Фронтенде ты показываешь количество продуктов. Пусть это значение получается асинхронно (может 1 или несколько запросов). Тогда чтобы описать получение количества продуктов ты пишешь код вида, где возвращаешь Promise. В момент возврата промис еще не зарезолвлен (не resolved), нет значения прикрепленного к нему. Зато у него есть методы .then, .catch через которые потребиталь может описать функции которые выполнятся когда в промисе появится значение (устанавливается с помощью вызова resolve).
function getProductsCount() {
return new Promise(function (resolve, reject) {
//...
//...
})
}
А потребитель количества продуктов описать что он будет делать когда значение вычислится (когда промис зарезолвится). Именно эту мезанику я имею в виду когда говорю “объект который дает тебе описать как работать с данными которые еще не появились программе”.
Использование в ajax-е - не единственное место для промисов. Они нужны почти в любом асинхронном API: например на сервере промисы ипользуются для описания результатов вызовов базы данных, файловой системы. С помощью промисов удобно моделировать множественные запросы в виде одного промиса: это когда нужно сделать 5-10 запросов на сервер и работать с результатом всех запросов.
Разные задачи, но не взаимоисключающие. Ситуация именно такая как выглядит: с помощью catch ты описываешь только обработчик для ошибки. С помощью then ты описываешь обработчик для успеха а так же можешь описать обработчик для ошибки.
Ты всегда передаешь функции в promise чтобы он их контролировал. Воспро оборачивания не стоит, обращай внимание где функции передаются аргументами.
Ты передаешь функцию в промис, он контролирует когда ее вызвать. При ее вызове он контролирует какие функции передать в аргументы вызванной функции. Эти функции будут в переменных resolve, reject и уже ты определяешь когда их вызывать. Их вызов даст сигнал промису что или значение получено или ошибка
const p = new Promise(function (resolve, reject) {
//....
})
И при использовании промиса через его методы ты тоже даешь функцию чтобы ее контролировал промис:
p.then(function (res) {
console.log(res)
})
var isNetworkOK = true;
function downloadFile(url) { // Обязательно оборачивать в функцию Promise? Что это даёт?
// это дает возможность получить сколько угодно промисов, параметрезировав запрос по url.
// если не использовать функции то ты опишешь промис для 1 конкретного урла.
// с функцией ты можешь описать получение промисов для разных урлов (соответственно загрузку любого файла по урлу)
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
setTimeout( function() {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // resolve всегда должен возвращать объект?
// нет. Это может быть примитив: число, boolean итд.
}, 5 * 1000);
} else {
var error = new Error('There is a problem with the network.');
reject(error);
}
}
);
return willIGetAFile; // Зачем возвращать Promise? Разве он не был возвращён на 18 строке? ( resolve(file) )
// забудь на секунду про промисы и посмотри на синтаксические конструкции как на функции и объекты
// в какой строке функция downloadFile возвращает значение? (подсказка - не в 18).
// теперь можешь вспомнить про промисы: return нужен чтобы результат работы функции был тем что справа от `return` - объект
// которым ниже потребитель будет пользоваться чтобы описать что делать с результатом.
// в18 строке устанавлиается значение промиса, это не тоже самое что вернуть объект промиса
}
// те же вопросы касаются и функции ниже
// те же ответы
function openFile(file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
function (resolve, reject) {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen;
}
console.log("Start app.."); // ***
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3"); // Что здесь происходит? Мы что, перетираем промис на 7 строчке?
// мы записываем в willIGetAFile инстанс промиса (это у которого методы then и catch)
// этот объект представляет "обещаение" данных, и мы можем описать что мы хотим делать с данными если/когда они появятся.
willIGetAFile // Здесь уже находится не промис, а downloadFile("http://example.com/file.mp3") И у него вызывается then... Почему?
// Здесь уже находится не промис, а downloadFile("http://example.com/file.mp3") - в чем разница?
// promise - это значение (вернее тип значения), downloadFile("http://example.com/file.mp3") - результат вызова функции, значение, значение типа промис. Тут нет противоречия
.then(openFile) // Chain it!
.then(function (fulfilled) { // If successful fileOpen.
// Get a message after file opened!
// Output: File file.mp3 opened!
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
console.log("End app.."); // ***
Спасибо за развёрнутый, но до конца не понимаю… Почему нельзя было написать так: downloadFile(“http://example.com/file.mp3”)
Зачем нужно было присваивать эту функцию в переменную, в которой лежит промис, который сам находится в этой функции…?
Я вас понимаю. Вы говорите о том, куда присвоит функция downloadFile(“http://example.com/file.mp3”) объект промис… Но почему тогда не использовать другую, свободную переменную? Ведь эта переменная занята!
function downloadFile(url) {(соответственно загрузку любого файла по урлу)
var willIGetAFile
willIGetAFile - эта переменная - локальная для функции downloadFile и никак не влияет на var willIGetAFile = downloadFile(“http://example.com/file.mp3”); переменную. Это просто совпадение что они имеют одинаковые имена. Так же можно было использовать переменную с любым другим именем.
Я пометил свой первый ответ как принятый ответ на вопрос (можешь поменять если считаешь иначе). Пока можем продолжить выяснять нюансы по твоим вопросам.
Промисы это альтернатива колбеку, с колбеком было так
test1 и test2 - некие асинхронные функции с колбеками
test1((result1) => {
...... что-то делаем
test2((result2) => {
...... что-то делаем и возможно тут внутри у нас еще какие асинхронные функции
})
})
в случае выше, велик риск попасть в колбекхел или как еще называют - лапшу, когда очень много вложенных функций и слева экрана у нас появиться огромный отступ. Такой код тяжело читать, что весьма важно.
Если же внутри test1 и test2 реализован промис, то это можно записать с колбеками но без постоянно возрастающих отступов
Promise.resolve().then(() => {
return test1();
}).then((result1) => {
......
retrun test2();
}).then((result2) => {
...... и так далее
}).catch(err => {
...... если же в асинхротронном потоке выше, где-то будет ошибка, она попадет в ближайший снизу catch, то-есть сюда
})
Помимо этого, так как колбеки это функции, а промисы все таки специальная вещь для работы с асинхронностью, у промисов куча ништяков для работы с асинхронностью
Ну а для тех кто не осилил промисы, и ситуаций когда надо надо просто дождаться результат асинхронной функции(промифицырованной), сделали еще и асинк авейты
внутри test1 и test2 реализован промис,
const run = async () => {
const result1 = await test1().catch(err => { ..... });
const result2 = await test2().catch(err => { ..... });
...... и так далее
});
или так
const run = async () => {
try {
const result1 = await test1().catch(err => { ..... });
const result2 = await test2().catch(err => { ..... });
...... и так далее
} catch (err) {
.........
}
});
Сама функция run при этом станет асинхронной и промифицырованной, но внутри можно делать псевдосинхронный код используя await