Застряв в аду обратного вызова, вот объект Promise, который обещает решить проблемы с функцией обратного вызова.

JavaScript по своей природе является синхронным языком. Асинхронные функции, такие как обратный вызов, обещание, являются внутренними для механизма JavaScript, который является частью браузера. Например, Chrome использует движок JavaScript Google V8. Из-за этого весь функционал, связанный с асинхронностью, разрабатывается во внешних библиотеках.

В JS мы можем решать асинхронные функции, используя Callback, Promises, Async Await.

Перед тем, как мы начнем, пожалуйста, прочтите статью Глубокое погружение в JavaScript.

Темы охватывали

  • Что такое обещания
  • Какой вариант использования решается обещаниями
  • Проблема с функциями обратного вызова, ад обратного вызова
  • Как написать код производителя обещаний
  • Свойства объекта обещания
  • Как написать потребительский код обещания
  • Решение ада обратных вызовов с обещаниями

Что такое обещания

Промисы — это объект JavaScript, представленный в ES6 и более поздних версиях, который обрабатывает асинхронный результат операции.

Он состоит из кода производителя, который выдает результат/ответ через некоторое время с момента его вызова. Другая часть называется кодом потребителя, который использует результат/ответ, сгенерированный кодом производителя.

Какой вариант использования решается обещаниями

  1. Предположим, мы вызываем API, и время ответа этого API велико. Это означает, что для обработки ответа нам нужно подождать, пока мы не получим ответ.
  2. Предположим, мы загружаем файл JavaScript в функцию, а в следующей строке мы вызываем функцию, которая объявлена ​​внутри этого скрипта.
  3. Предположим, вы хотите вызвать другой API после получения первого ответа, а еще один — после получения второго ответа API. Этот тип требования называется связывание обратных вызовов.

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

Проблема с функциями обратного вызова, ад обратного вызова

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

function getAPIRequest(callbackOnSuccess, callbackOnError){  var getReq = new XMLHttpRequest();
  getReq.open("GET", "https://reqres.in/api/users?page=2");
  getReq.send();
  getReq.onload = () => {
    alert("The response is " + request.response);
  }
}
getAPIRequest();

В приведенном выше коде мы вызвали GET API, и после получения ответа мы вызываем метод processSuccessResponse в качестве обратного вызова. Это кажется правильным, но предположим, что мы хотим вызвать второй API, когда получим первый ответ API, аналогично третий API, когда получим первый ответ API. Давайте рассмотрим это в качестве примера, сейчас мы будем вызывать тот же API:

function getAPIRequest(callbackOnSuccess, callbackOnError){  
  var getReq1 = new XMLHttpRequest();
  getReq1.open("GET", "https://reqres.in/api/users?page=2");
  getReq1.send();
  getReq1.onload = () => {
    alert("The response is " + request.response);
    var getReq2 = new XMLHttpRequest();
    getReq2.open("GET", "https://reqres.in/api/users?page=2");
    getReq2.send();
    getReq2.onload = () => {
      alert("The response is " + request.response);
      var getReq3 = new XMLHttpRequest();
      getReq3.open("GET", "https://reqres.in/api/users?page=2");
      getReq3.send();
      getReq3.onload = () => {
        alert("The response is " + request.response);
        //another api call or some other processing
      }
    }
  }
}
getAPIRequest();

Как мы видим, эта вещь может стать очень большой, и ее станет трудно читать, поддерживать код и понимать, возникнут ли какие-либо проблемы позже.

Промис — это ответ на вышеописанную проблему, у него есть потребительский код, который является своего рода обратным вызовом, и все потребители могут быть написаны отдельно, и они будут выполняться один за другим.

Как написать код производителя обещаний

Код производителя обещания — это часть, которая производит некоторый результат или выходные данные, которые позже могут быть обработаны потребителем. Давайте попробуем вызвать GET API, который похож на код нашего производителя, и его результатом будет ответ, который мы получаем от API.

function getFirstAPIRequest() {
  return new Promise(function(resolve, reject) {
    var getReq = new XMLHttpRequest();
    getReq.open("GET", "https://reqres.in/api/users?page=2");
    getReq.send();
    getReq.onload = () => {
      if(getReq.status == 200){
        resolve(getReq.response);
      }else{
        reject(new Error("There was some problem in the request."));
      }
    }
  });
}

Примечание. Откройте https://playcode.io/ и вставьте этот код. Если вы хотите научиться, попрактикуйтесь с этим кодом.

Функция, передаваемая Promise, называется executor. Она запускается автоматически при создании промиса. Разрешение и отклонение — это обратные вызовы, предоставляемые самим JavaScript.

Когда исполнитель получает результат, мы можем вызывать эти обратные вызовы в зависимости от нашего требования. Как и в нашем случае, мы вызвали resolve, если статус ответа равен 200, иначе мы вызываем reject.

Свойства объекта обещания

Объект Promise, созданный с помощью новой клавиатуры, имеет два свойства: state и result.

Они меняют свои значения следующим образом

  • Сначала состояние будет отложено, если будет вызвано разрешить, оно изменится на разрешено состояние, иначе, если будет вызвано отклонить он изменится на отклонено.
  • result сначала будет неопределенным, response, когда вызывается resolve(response), иначе error, когда reject (ошибка) вызывается.

Как написать потребительский код обещания

Если мы хотим использовать ответ, мы можем сделать это тремя способами в Promise, используя then, catch и finally.

  • затем потребительский код

Метод then Promise принимает два аргумента функции: первый выполняется при вызове resolve, а второй — при вызове reject .

function getFirstAPIRequest() {
  return new Promise(function(resolve, reject) {
    var getReq = new XMLHttpRequest();
    getReq.open("GET", "https://reqres.in/api/users?page=2");
    getReq.send();
    getReq.onload = () => {
      if(getReq.status == 200){
        resolve(getReq.response);
      }else{
        reject(new Error("There was some problem in the request."));
      }
    }
  });
}
getFirstAPIRequest()
  .then((response) => console.log(response), (error) => console.log
("Cannot process response"));

Это приведет к следующему результату.

Если мы хотим использовать только обратный вызов разрешения, мы можем удалить определение второго метода:

getFirstAPIRequest()
  .then((response) => console.log(response));

Если мы хотим использовать только обратный вызов отклонения, мы можем передать null в качестве первого аргумента и второго любого определения функции.

getFirstAPIRequest()
  .then(null, (error) => console.log
("Cannot process response"));
  • поймать потребительский код

Для обработки отказа обратного вызова обещание имеет один специальный метод, называемый catch. Мы можем написать внутри него наше определение, которое будет вызываться только в случае вызова обратного вызова reject.

getFirstAPIRequest()
  .catch((errorMsg) => console.log(errorMsg));
  • наконец-то Потребительский кодекс

Могут быть случаи, когда мы хотим закрыть некоторые ресурсы до завершения обратного вызова. Тогда мы используем finally, у него есть только определение функции (без каких-либо аргументов), которая всегда будет вызываться. После завершения выполнение будет передано следующей функции.

getFirstAPIRequest()
  .finally(() => console.log("Closing important resources))
  .then((response) => console.log(response), (error) => console.log
("Cannot process response"));

Теперь предположим случай цепочки обратных вызовов, когда мы хотим вызвать другой API на основе ответа от первого API. Давайте рассмотрим пример:

function getFirstAPIRequest() {
  console.log("Processing first call");
  return new Promise(function(resolve, reject) {
    var getReq = new XMLHttpRequest();
    getReq.open("GET", "https://reqres.in/api/users?page=2");
    getReq.send();
    getReq.onload = () => {
      resolve(getReq.response);
    }
  });
}
function getSecondAPIRequest(firstResponse) {
  //do some processing on response, here I am passing the first simply
  console.log("Processing second call");
  return new Promise(function(resolve, reject) {
    var getReq = new XMLHttpRequest();
    getReq.open("GET", "https://reqres.in/api/users?page=2");
    getReq.send();
    getReq.onload = () => {
      resolve(getReq.response);
    }
  });
}
getFirstAPIRequest()
  .then((response) => getSecondAPIRequest(response))
  .then((response) => console.log(response));

Это приведет к следующему результату.

Я надеюсь, что все приведенные выше примеры помогут вам понять, что такое Promise в JavaScript, чем он лучше, чем Callback, так как изящно справляется с Callback Hell.

Дошли до этого места, дайте мне следить за последними историями.

Если вам понравилось это читать, не забудьте про аплодисменты. 👏
Спасибо.