JavaScript | Передача параметров в функцию обработки отклонённого обещания

JavaScript | Передача параметров в функцию обработки отклонённого обещания

Как вынести отдельно из методов then() или catch() функцию обработки отклонённого обещания (Promise Rejected) с поддержкой передачи нужного количества параметров при помощи bind()?

Что нужно запомнить?

Метод bind() всегда принимает первым параметром объект, в контексте которого будет вызвана функция.

Если мы просто указываем ссылку на функцию в методах then() или catch(),  то эта функция просто примет первым (одним) параметром результат отклонения обещания. Остальные параметры функции будут undefined, если она (функция) ожидала их получить и с ними поработать. В общем по умолчанию 1 параметр. Если нужно передать не один параметр а несколько, тогда нужен bind().

Если мы в bind() передадим, кроме первого параметра нужное количество параметров для функции, тогда эта функция отработает корректно по своей задуманной логике.

Но если мы передадим в bind() недостаточное количество параметров, тогда результат из then() или catch() может попасть не в тот обработчик (не на своё место). Количество ожидаемых и переданных параметров может отличаться. Это влияет на расположение ожидаемого параметра, относительно добавляемых дополнительно в функцию.

По умолчанию обработчики отклонений в then() или catch() ждут ОДИН параметр!

 

Давайте перейдём к примерам, чтобы было понятнее.

 

Функция ждёт 3 параметра, но мы просто ссылаемся на неё — без bind()

let res = new Promise((resolve, reject)=>{ resolve(1) });
let rej1 = new Promise((resolve, reject)=>{ reject(0) });
let rej2 = new Promise((resolve, reject)=>{ reject('000') });

function f1(q,w,e){
  console.log(`q`,q);
  console.log(`w`,w);
  console.log(`e`,e);
};

Promise.all([res, rej2, rej1]).then(q=>q, f1);
Promise.all([res, rej2, rej1]).catch(f1);

Лог консоли:

Функция обработки отклонённого обещания без bind() - JavaScript
Функция обработки отклонённого обещания без bind() — JavaScript

По итогу функция «f1» будет вызвана только с одним параметром — с «q«, который пришёл с результатом отклонения. Параметры «w» и «e» будут иметь значение undefined.

Важно помнить, что если отклонений в Promise.all() фактически много, то сработает самое первое и именно результат этого отклонения и будет передан. Остальные отклонения обещаний передаваться не будут.

Конкретно этот пример синтетический и мы стабильно получаем строку ‘000‘, но в реальном асинхронном мире может быть любое из отклонённых обещаний.

 

Функция ждёт 3 параметра, но мы указываем объект контекста вызова через bind()не передаём ничего кроме this из результата отклонения

Эта ситуация будет равносильна предыдущей. Но мы её рассматриваем, чтобы наработать «насмотренность кода».

let res = new Promise((resolve, reject)=>{ resolve(1) });
let rej1 = new Promise((resolve, reject)=>{ reject(0) });
let rej2 = new Promise((resolve, reject)=>{ reject('000') });

function f1(q,w,e){
   console.log(`q`,q);
   console.log(`w`,w)
   console.log(`e`,e)
};

Promise.all([res, rej2, rej1]).then(q=>q, f1.bind(this));
Promise.all([res, rej2, rej1]).catch(f1.bind(this));

Лог консоли:

Функция обработки отклонённого обещания с bind() и только this - JavaScript
Функция обработки отклонённого обещания с bind() и только this — JavaScript

Метод bind() в любом случае первым параметром принимает объект, который будет использован функцией как свой this.

В момент вызова bind() мы передаём текущий this  — это глобальный объект (window или global в зависимости клиент это или сервер). Но самих параметров для функции не передаём.

В результате в этом примере мы также вызовем «f1» с одним параметром.

 

Функция ждёт 3 параметра, но мы указываем объект контекста вызова через bind()передаём this и один параметр

ВНИМАНИЕ! В этой ситуации мы заметим сдвиг параметров, который при первом просмотре может ввести в заблуждение. Здесь просто нужно очень хорошо понимать как работает сам bind(), then() и catch() со своими параметрами.

let res = new Promise((resolve, reject)=>{ resolve(1) });
let rej1 = new Promise((resolve, reject)=>{ reject(0) });
let rej2 = new Promise((resolve, reject)=>{ reject('000') });

function f1(q,w,e){
  console.log(`q`,q);
  console.log(`w`,w)
  console.log(`e`,e)
};

Promise.all([res, rej2, rej1]).then(q=>q, f1.bind(this, 'qqq'));
Promise.all([res, rej2, rej1]).catch(f1.bind(this, 'qqq'));

Лог консоли:

Функция обработки отклонённого обещания с bind(), this и ещё одним параметром JavaScript
Функция обработки отклонённого обещания с bind(), this и ещё одним параметром JavaScript

В этот раз до функции «f1» дошли два параметра. Первым в неё пришёл тот, который был передан вторым в bind(). Поэтому мы видим в логе «q qqq«.

В идеале, то что мы хотим по факту передать в функцию, должно следовать со второго параметра в bind().

НО! Но если передаваемых параметров не хватает для функции, то метод then() или catch() подпихнёт туда свой результат отклонения последним параметром. В нашем случае не хватает ещё два параметра для функции. В результате then() или catch() подставляет результат отклонения обещания и вторым консольным логом мы получаем «w 000«.

Третьего параметра не приходит в функцию так как мы передали один, а then() или catch() уже отдал свой. Поэтому третий лог в консоль «e undefined«.

Важно! Ожидаемый параметр будет добавлен в самый конец из всех возможных переданных параметров!

 

Функция ждёт 3 параметра, но мы указываем объект контекста вызова через bind()передаём this и два параметра

По аналогии с предыдущим.

let res = new Promise((resolve, reject)=>{ resolve(1) });
let rej1 = new Promise((resolve, reject)=>{ reject(0) });
let rej2 = new Promise((resolve, reject)=>{ reject('000') });

function f1(q,w,e){
  console.log(`q`,q);
  console.log(`w`,w)
  console.log(`e`,e)
};

Promise.all([res, rej2, rej1]).then(q=>q, f1.bind(this, 'qqq', 'www'));
Promise.all([res, rej2, rej1]).catch(f1.bind(this, 'qqq', 'www'));

Лог консоли:

Функция обработки отклонённого обещания с bind(), this и ещё двумя параметрами - JavaScript
Функция обработки отклонённого обещания с bind(), this и ещё двумя параметрами — JavaScript

В этот раз мы пытаемся вызвать функцию с двумя параметрами, но then() или catch() подсовывает туда свой параметр и по итогу функция вызывается с тремя параметрами.

 

Функция ждёт 3 параметра, но мы указываем объект контекста вызова через bind()передаём this и три параметра

Ну и наконец нужный нам вариант (опционально под задачу). Функция получает полный набор параметров для своего вызова, без сдвигов из-за then() или catch().

let res = new Promise((resolve, reject)=>{ resolve(1) });
let rej1 = new Promise((resolve, reject)=>{ reject(0) });
let rej2 = new Promise((resolve, reject)=>{ reject('000') });

function f1(q,w,e){
   console.log(`q`,q);
   console.log(`w`,w)
   console.log(`e`,e)
};

Promise.all([res, rej2, rej1]).then(q=>q, f1.bind(this, 'qqq', 'www', 'eee'));
Promise.all([res, rej2, rej1]).catch(f1.bind(this, 'qqq', 'www', 'eee'));

Лог консоли:

Функция обработки отклонённого обещания с bind(), this и всеми параметрами - JavaScript
Функция обработки отклонённого обещания с bind(), this и всеми параметрами — JavaScript

В этой ситуации мы полностью вытеснили результат отклонения обещания. Ему просто не нашлось места в функции. Такое поведение может быть не применимо на практике, но нужно знать что оно возможно в JavaScript так в этом языке нет ограничения на передаваемые параметры в функцию.

 

Итог

Задачи могут быть разными. В этой публикации мы обсудили возможность вынесения функции из синтаксической конструкции методов then() и catch().

Мы это сделали для того, чтобы улучшить читаемость кода. Чтобы не нагромождать вложенность функций в функции. Чтобы функция обработки результата отклонения обещания могла принять не только сам результат отклонения, но и что-то ещё, из того что мы хотим.

При добавлении параметров для вызова через bind() ожидаемый функцией параметр будет в САМОМ КОНЦЕ, а не в начале, как может показаться.

 

Информационные ссылки

Стандарт ECMAScript мульти-страничная версия — https://tc39.es/ecma262/multipage/

Метод bind()https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-function.prototype.bind

Метод then()https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise.prototype.then

Метод catch()https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise.prototype.catch