Javascript метод promise.then()

Promise.all

Допустим, нам нужно запустить множество промисов параллельно и дождаться, пока все они выполнятся.

Например, параллельно загрузить несколько файлов и обработать результат, когда он готов.

Для этого как раз и пригодится .

Синтаксис:

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

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

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

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

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

Например, если у нас есть массив ссылок, то мы можем загрузить их вот так:

А вот пример побольше, с получением информации о пользователях GitHub по их логинам из массива (мы могли бы получать массив товаров по их идентификаторам, логика та же):

Если любой из промисов завершится с ошибкой, то промис, возвращённый , немедленно завершается с этой ошибкой.

Например:

Здесь второй промис завершится с ошибкой через 2 секунды. Это приведёт к немедленной ошибке в , так что выполнится : ошибка этого промиса становится ошибкой всего .

В случае ошибки, остальные результаты игнорируются

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

Например, если сделано несколько вызовов , как в примере выше, и один не прошёл, то остальные будут всё ещё выполняться, но за ними уже не смотрит. Скорее всего, они так или иначе завершатся, но их результаты будут проигнорированы.

ничего не делает для их отмены, так как в промисах вообще нет концепции «отмены». В главе Fetch: прерывание запроса мы рассмотрим , который помогает с этим, но он не является частью Promise API.

разрешает передавать не-промисы в (перебираемом объекте)

Обычно, принимает перебираемый объект промисов (чаще всего массив). Но если любой из этих объектов не является промисом, он передаётся в итоговый массив «как есть».

Например, здесь результат:

Таким образом, мы можем передавать уже готовые значения, которые не являются промисами, в , иногда это бывает удобно.

Концепция 4: Async и await

Async-await позволяет вызывать асинхронные функции синхронно. Рассмотрим этот момент более подробно.

Асинхронные функции – это функции, которые переходят в состояние ожидания при вызове, а разрешение зависит от результата выполнения.

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

Предположим, что у нас есть две асинхронные функции:

  • getManagerByEmployeeId: принимает employeeId в качестве входных параметров и возвращает managerId.
  • getManagerNameById:принимает в качестве входных параметров managerId и возвращает имя менеджера.

Наша задача – получить имя менеджера для данного идентификатора сотрудника. То есть, реализовать функцию getManagerName в приведенном ниже коде.

const EmployeeIDManagerIdMap = {
 "AA234": "0AA316",
 "BBCD5":"4AA354"
};const ManagerIdManagerNameMap = {
 "0AA316":"John Doe",
 "4AA354":"Ravindram S"
};function getManagerByEmployeeId(employeeId) {
  return new Promise((resolve, reject)=> {
    setTimeout(()=>{
      if(EmployeeIdManagerIdMap) {
       resolve(EmployeeIdManagerIdMap);
      } else {
       reject(`Invalid employee id ${employeeId}`);
      }
    },2000);
  });
}function getManagerNameById(managerId) {
    return new Promise((resolve, reject)=> {
    setTimeout(()=>{
      if(ManagerIdManagerNameMap) {
       resolve(ManagerIdManagerNameMap);
      } else {
       reject(`Invalid manager id ${managerId}`);
      }
    },2000);
  });
}// получаем имя менеджера по employeeId
function getManagerName(employeeId) {
  // возвращаем имя менеджера
}

Один из способов сделать это – использовать структуру вложенных промисов и преобразовать getManagerName в промис.

function getManagerName(employeeId){
return new Promise((resolve, reject)=>{
getManagerByEmployeeId(employeeId).then(function(managerId){
getManagerNameById(managerId).then(function(managerName){
resolve(managerName);
}, function(error){
reject(error);
})
}, function(error){
reject(error);
})
})
}

Это создаст структуру вложенного кода, которая сложна для понимания и заставляет использовать промис в функции getManagerName.

Теперь рассмотрим реализацию концепции async-await.

async function getManagerName(employeeId){
try {
let managerId = await getManagerByEmployeeId(employeeId);
try {
let managerName = await getManagerNameById(managerId);
return managerName;
} catch(error) {
console.error("getManagerNameById promise rejected", error);
}
} catch(error){
console.error("getManagerByEmployeeId promise rejected", error);
}
}

Благодаря ей код стал проще и нам не нужно использовать промис в функции getManagerName.

Конструкция await откладывает выполнение кода до тех пор, пока не будет разрешена вызываемая асинхронная функция.

Каждая функция, содержащая await, должна быть объявлена ​​асинхронной с использованием ключевого слова async.

Но отклоненный промис выдает ошибку при вызове функции. Поэтому его нужно обработать с помощью блока try-catch, окружающего вызов асинхронной функции.

Promise How To

Here is how to use a Promise:

myPromise.then(
  function(value) { /* code if successful */ },
  function(error) { /* code if some error */ }
);

Promise.then() takes two arguments, a callback for success and another for failure.

Both are optional, so you can add a callback for success or failure only.

Example

function myDisplayer(some) {
  document.getElementById(«demo»).innerHTML = some;}
let myPromise = new Promise(function(myResolve, myReject) {
  let x = 0;
// The producing code (this may take some time)
  if (x == 0) {
    myResolve(«OK»);
  } else {
    myReject(«Error»);
  }
});
myPromise.then(
  function(value) {myDisplayer(value);},
  function(error) {myDisplayer(error);});

Promise: Последовательные итерации

На данный момент, на примере приложения веб­-паука, мы рассмотрели объекты Promise и приемы их использования для создания простой элегантной реализации последовательного потока выполнения. Но этот код обеспечивает выполнение лишь известного заранее набора асинхронных операций. Поэтому, чтобы восполнить пробелы в исследовании последовательного выполнения, нам нужно разработать фрагмент, реализующий итерации с помощью объектов Promise. И снова прекрасным примером для демонстрации станет функция spiderLinks().

function spiderLinks(currentUrl, body, nesting) {
  let promise = Promise.resolve();
  if(nesting === 0) {
    return promise;
  }
  const links = utilities.getPageLinks(currentUrl, body);
  links.forEach(link => {
    promise = promise.then(() => spider(link, nesting – 1));
  });
  return promise;
}

Для асинхронного обхода всех ссылок на веб-­странице нужно динамически создать цепочку объектов Promise.

  1. Начнем с определения «пустого» объекта Promise, разрешаемого как undefned. Он будет служить началом цепочки.
  2. Затем в цикле присвоим переменной promise новый объект Promise, полученный вызовом метода then() предыдущего объекта Promise в цепочке. Это и есть шаблон асинхронных итераций с использованием объектов Promise.

В конце цикла переменная promise будет содержать объект Promise, который вернул последний вызов then() в цикле, поэтому он будет разрешен после разрешения всех объектов Promise в цепочке.

Применяем промисы

Теперь у нас есть промис, давайте применим его:

1. Мы вызываем функцию в . В этой функции, мы применим наш промис .

2. Нам надо сделать одно действие, чтобы промис был решен или отклонен, тут мы будем использовать  и .

3. В нашем примере, у нас в . Какое значение у ?  значение это точное значение в вашем промисе (значение при успехе). Следовательно, это будет .

4. У нас есть  в . Какое значение будет у ? Как вы могли предположить,  значение именно то, которое вы указали в промисе (значение при неудаче). Следовательно, в этом случае это будет .

Давайте запустим этот пример и увидим результат!

Цепочки промисов

Да, в промисах есть цепочки.

Давайте представим, что вы ребенок и обещали своему другу, что покажете ему новый телефон, когда вам его купят. Это будет ещё один промис.

В этом примере вы уже наверное поняли, что мы не вызывали . Так как, в принципе, это опционально.

Мы вообще можем сократить этот пример, используя .

А теперь давайте свяжем наши промисы. Вы — ребенок и можете запустить  промис только после промиса .

Вот так легко связывать промисы.

Перехват ошибок

Выше мы рассмотрели «идеальный случай» выполнения, когда ошибок нет.

А что, если github не отвечает? Или JSON.parse бросил синтаксическую ошибку при обработке данных?

Да мало ли, где ошибка…

Правило здесь очень простое.

При возникновении ошибки – она отправляется в ближайший обработчик .

Такой обработчик нужно поставить через второй аргумент или, что то же самое, через .

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

В примере выше ошибка возникает в первом же , но с тем же успехом поймал бы ошибку во втором или в .

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

А что после ?

Обработчик получает ошибку и должен обработать её.

Есть два варианта развития событий:

  1. Если ошибка не критичная, то возвращает значение через , и управление переходит в ближайший .
  2. Если продолжить выполнение с такой ошибкой нельзя, то он делает , и тогда ошибка переходит в следующий ближайший .

Это также похоже на обычный – в блоке ошибка либо обрабатывается, и тогда выполнение кода продолжается как обычно, либо он делает . Существенное отличие – в том, что промисы асинхронные, поэтому при отсутствии внешнего ошибка не «вываливается» в консоль и не «убивает» скрипт.

Ведь возможно, что новый обработчик будет добавлен в цепочку позже.

Конструктор Promise, его философия. Callback функция executor — как «выполнитель» обещания. Схема взаимодействия: Promise ( конструктор ) — executor ( callback ) — promise ( объект )

Итак, мы выяснили, что promise — это сущность, которая технически представляет собой JS объект с особыми скрытыми внутренними полями, которые в свою очередь обеспечивают философское наполнение смыслом слова «обещание».

Когда новичок первый раз создает объект promise, то его ожидает следующая картина (рис. 5).

рис 5. ( Самый первый раз интуитивно создаем promise объект )

Что пошло не так, и почему ошибка — стандартный вопрос. При ответе на него лучше снова привести какую-то аналогию из жизни. Например, мало кто любит «пустозвонов» вокруг нас: которые только обещают, но ничего не делают по выполнению своих заявлений (политика не в счет). Мы намного лучше относимся к тем людям, которые после своего обещания имеют план и предпринимают сразу же какие-то действия для достижения обещанного результата.

Так и философия ECMAScript подразумевает, что если вы создаете обещание, то сразу же укажите, как вы его будете выполнять. Свой план действий программисту необходимо оформить в виде параметра-функции, которую передадите в конструктор Promise. Следующий эксперимент выглядит так (рис. 6).

рис 6. ( Создаем promise объект, передавая в конструктор Promise функцию executor )

Из подписи к рисунку мы видим, что функция (параметр конструктора Promise) имеет собственное название — executor. Её задача — начать выполнение обещания и, желательно, привести его к какому-то логическому завершению. И если программист может писать какой угодно код в executor-е, то как программисту просигнализировать JS-у, что все — работа сделана — можно идти и смотреть результаты обещания?

Маркеры или сигналы, которые помогут программисту сообщить, что обещание завершено, передаются автоматически в параметры executor-a в виде аргументов, специально сформированных JavaScript-ом. Эти параметры можно называть как угодно, но чаще всего вы встретите их под такими именами, как res и rej. В спецификации ECMAScript их полное название — resolve function и reject function. Эти маркеры-функции имеют свои особенности, которые рассмотрим чуть ниже.

Для осознания новой информации новичку предлагается самостоятельно закодировать следующее утверждение: «Обещаю, что смогу разделить одно число на другое и выдать ответ, если только делитель не ноль». Вот как будет выглядеть приблизительно такой код (рис. 7).

рис 7. ( Решение задачи на деление 2-х чисел через промисы )

Теперь можно проанализировать полученный результат. Мы видим, что уже второй раз консоль браузера показывает объект промис в интересном виде. А именно: указаны 2 дополнительных поля в двойных квадратных скобках. Можно спокойно провести аналогию между `PromiseState` и `PromiseStatus`, fulfilled и resolved, `PromiseValue` и `PromiseResult`. Да, браузер сам пытается подсказать программисту о наличии и значении внутренних полей promise объекта. Также мы видим воедино связанную систему объекта promise, функции executor, специальных функций-callback-маркеров res и rej.

Чтобы ученик / напарник раскрепостился еще больше в этом материале, ему предлагается следующий код (рис. 8). Необходимо его проанализировать и ответить на следующие вопросы.

рис 8. ( Вариация решения задачи на деление 2-х чисел через промисы )

Отработает ли код? Где здесь функция executor и какое она имеет имя? Подходящее ли в этом коде название «wantToDivide»? Что возвращает после себя функция bind? Почему в функцию bind аргументы передаются только на втором и третьем месте? Куда исчезли специальные функции resolve function и reject function? Каким образом необходимые вводные данные number1 и number2 попали в «план выполнения обещания»? Сколько элементов в псевдомассиве «arguments»? Можно ли по памяти восстановить то, как будет выглядеть ответ в консоли браузера?

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

Promise.allSettled

A recent addition

This is a recent addition to the language.
Old browsers may need polyfills.

rejects as a whole if any promise rejects. That’s good for “all or nothing” cases, when we need all results successful to proceed:

just waits for all promises to settle, regardless of the result. The resulting array has:

  • for successful responses,
  • for errors.

For example, we’d like to fetch the information about multiple users. Even if one request fails, we’re still interested in the others.

Let’s use :

The in the line above will be:

So for each promise we get its status and .

If the browser doesn’t support , it’s easy to polyfill:

In this code, takes input values, turns them into promises (just in case a non-promise was passed) with , and then adds handler to every one.

That handler turns a successful result into , and an error into . That’s exactly the format of .

Now we can use to get the results of all given promises, even if some of them reject.

Understanding JavaScript Promises

In JavaScript, a promise is an object that returns a value which you hope to receive in the future, but not now.

Because the value will be returned by the promise in the future, the promise is very well-suited for handling asynchronous operations.

It’ll be easier to understand the concept of JavaScript promises through an analogy.

Suppose that you promise to complete learning JavaScript by next month.

You don’t know if you will spend your time and effort to learn JavaScript until next month. You can either be completing learning JavaScript or not.

A promise has three states:

  • Pending: you don’t know if you will complete learning JavaScript by the next month.
  • Fulfilled: you complete learning JavaScript by the next month.
  • Rejected: you don’t learn JavaScript at all.

A promise starts in the pending state which indicates that the promise hasn’t completed. It ends with either fulfilled (successful) or rejected (failed) state.

Waiting for a file

Example using Callback

function getFile(myCallback) {
  let req = new XMLHttpRequest();
  req.open(‘GET’, «mycar.html»);
  req.onload = function() {
    if (req.status == 200) {
      myCallback(req.responseText);
    } else {
      myCallback(«Error: » + req.status);
    }
  }
  req.send();
}
getFile(myDisplayer);

Example using Promise

let myPromise = new Promise(function(myResolve, myReject) {
 
let req = new XMLHttpRequest();
 
req.open(‘GET’, «mycar.htm»);
 
req.onload = function() {
   
if (req.status == 200) {
     
myResolve(req.response);
   
} else {
     
myReject(«File not Found»);
   
}
  };
  req.send();
});
myPromise.then(
 
function(value) {myDisplayer(value);},
 
function(error) {myDisplayer(error);}
);

Create a Promise

To create a promise object, we use the constructor.

The constructor takes a function as an argument. The function also accepts two functions and .

If the promise returns successfully, the function is called. And, if an error occurs, the function is called.

Let’s suppose that the program below is an asynchronous program. Then the program can be handled by using a promise.

Example 1: Program with a Promise

Output

Promise {<resolved>: "There is a count value."}

In the above program, a object is created that takes two functions:  and .  is used if the process is successful and  is used when an error occurs in the promise.

The promise is resolved if the value of count is true.

Working of JavaScript promise

Цепочка промисов

Промис можно вернуть другому промису и таким образом создать цепочку промисов.

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

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

В этом примере мы вызываем , чтобы получить данные о моем профиле на github, и создаем цепочку промисов.

Запуск возвращает , который имеет много свойств, из которых мы используем:

  • — числовое значение, предоставляющее код состояния HTTP;
  • — сообщение о состоянии, которое , если запрос был выполнен успешно.

У также есть метод , который возвращает промис. Выполнив его он содержит нужный нам контент тела, обработанный и преобразованный в JSON.

Таким образом, учитывая эти промисы, вот что происходит: первый промис в цепочке — это определенная нами функция, называемая , которая проверяет статус ответа и, если ответ успешный (между 200 и 299), то выполнится , в противном случае .

Если выполняется , то цепочка промисов пропустит все перечисленные и перейдет непосредственно к первому внизу, в котором будет записан текст (Ошибка запроса) вместе с сообщением об ошибке.

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

В этом случае мы возвращаем данные, обработанные JSON, поэтому третий промис получает JSON напрямую:

это мы и выводим в консоль.

Using the Promise Object

Promises provide an alternative to callback-passing. Asynchronous functions return a object onto which callbacks can be attached.

Callbacks are attached using the method. They will be called when the promise is resolved.

var p = asyncfoo(a, b, c);

p.then(function(error, result) {
    if (error) return;
    alert(result);
});

Asynchronous functions must resolve the promise with the method when their task is done. This invokes the promise callback(s) with the same arguments that were passed to .

function asyncfoo() {

    var p = new promise.Promise();  /* (1) create a Promise */

    setTimeout(function() {
        p.done(null, "O hai!");     /* (3) resolve it when ready */
    }, 1000);

    return p;                       /* (2) return it */
}

Практический пример использования обещаний (promise) в JavaScript

В качестве примера будем перебирать массив объектов с целью получения необходимого нам значения. Массив будет представлять собой список продуктов на первую половину недели:

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

Для начала создадим JavaScript промис, который обернем в функцию getProduct():

В результате у нас есть функция, с двумя параметрами — список дней недели и искомый день. Внутри определено JavaScript обещание с циклом for..of перебирающий дни. В случае совпадения возвращается resolve() с массивом продуктов, а если день не найден, то отклоняется методом reject() с описанием причины.

После этого вызовем функцию с переданными в нее аргументами:

В результате того что мы ищем день, который есть в массиве, нам вернется список продуктов, с которым мы можем работать в методе .then() под #2.

Как можно заметить в цепочке #2 в метод передаются два аргумента — это функции fulfilled и rejected. Первая вызовется в случает успешного завершения метода под #1, а вторая отработает в случае если в предыдущих цепочках обещания было вызвано исключение или ошибка. Это завершит обещание, не дав отработать методу catch().

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

Таким образом, до появления js промисов подобные задачи решaли с помощью коллбеков. Это подход тоже рабочий, но при необходимости в одной операции вызывать сразу несколько обратных функций со временем код становился менее разборчивым и как следствие тяжело поддерживаемым.

Важно понимать, что JavaScript промисы не призваны заменить callback функции, а лишь дают нам возможность структурировать код при их использовании

Плюсы и минусы в теории

Async/awaitПлюсы

  • Удобство и простота чтения
  • Возможность использования последовательного стиля программирования

Минусы

  • Легко наткнуться на избыточное ожидание последовательного кода. Для истинной параллельности нужно модифицировать код.
  • Неочевидность возвращаемых значений try…catch.

PromiseПлюсы

  • Использует традиционный подход колбэков.
  • Данные с ошибками и данные с успешным результатом операции однозначно понимаемы.
  • Возможность использовать Promise.all без оглядки на синтаксис.
  • Оповещения Promise.resolve и Promise.reject доступны везде.
  • Наглядное использование метода Promise.finally.

Минусы

При неправильном использовании возможно создание слишком глубоких использований цепочек .then

Async функции

Добавление async-функций в ES2017 (ES8) сделало работу с промисами легче.

Важно отметить, что async-функции работают поверх промисов.
Эти функции не являются принципиально другими концепциями.
Async-функции были задуманы как альтернатива коду, использующему промисы.
Используя конструкцию async/await, можно полностью избежать использование цепочек промисов.
С помощью async-функций возможно организовать работу с асинхронным кодом в синхронном стиле.

Как видите, знание промисов всё же необходимо для понимания работы async/await.

Синтаксис

Синтаксис состоит из двух ключевых слов: и . Первое делает функцию асинхронной. Именно в таких функциях разрешается использование . Использование в любом другом случае вызовет ошибку.

Обратите внимание, что вставляется в начале объявления функции, а в случае стрелочной функции — между знаком и скобками. Async-функции могут быть помещены в объект в качестве методов или же просто использоваться в объявлении класса

Async-функции могут быть помещены в объект в качестве методов или же просто использоваться в объявлении класса.

Примечание Конструкторы класса и геттеры/сеттеры не могут быть асинхронными.

Async-функции всегда возвращают промисы

Функция возвращает строку . Т. к. это асинхронная функция, значение строки обёртывается в промис (с помощью конструктора).

Код выше можно переписать и без использования :

В таком случае, вместо , код вручную возвращает промис.

Тело асинхронной функции всегда обёртывается в новый промис

Если возвращаемое значение является примитивом, async-функция возвращает это значение, обёрнутое в промис. Но если возвращаемое значение и есть объект промиса, его решение возвращается в новом промисе.

Что происходит, когда внутри асинхронной функции возникает какая-нибудь ошибка?

Если ошибка не будет обработана, вернёт промис с реджектом. В таком случае вместо вернётся , содержащий ошибку.

Суть async-функций в том, что что бы вы не возвращали, на выходе вы всегда будете получать промис.

Асинхронные функции приостанавливаются при каждом await выражении

сказывается на выражениях. Если выражение является промисом, то async-функция будет приостановлена до тех пор, пока промис не выполнится. Если же выражение не является промисом, то оно конвертируется в промис через и потом завершается.

Как работает функция?

  1. После вызова функции первая строка конвертируется из в .
  2. После использования , выполнение функции приостанавливается, пока не получит своё значение (в данном случае это 9).
  3. приостанавливает выполнение функции, пока не завершится сама (после 1 секунды). Это, фактически, можно назвать остановкой функции на 1 секунду.
  4. Также через возвращает случайное значение, которое присваивается переменной .
  5. Случай с переменной идентичен случаю переменной . После этого опять происходит пауза на 1 секунду, но теперь ничего не возвращает, т. к. этого не требуется.
  6. Под конец эти значения считаются по формуле . Результат обёртывается в промис с помощью и возвращается функцией.

Примечание Если такие паузы напоминают вам генераторы в ES6, то на это есть свои причины.

AJAX Functions Included

Because AJAX requests are the root of much asynchrony in Javascript, promise.js provides the following functions:

promise.get(url, data, headers)
promise.post(url, data, headers)
promise.put(url, data, headers)
promise.del(url, data, headers)

(optional) : a {key: value} object or url-encoded string.

(optional) : a {key: value} object (e.g. ).

Example:

promise.get('/').then(function(error, text, xhr) {
    if (error) {
        alert('Error ' + xhr.status);
        return;
    }
    alert('The page contains ' + text.length + ' character(s).');
});

You can set a time in milliseconds after which unresponsive AJAX
requests should be aborted. This is a global configuration option,
disabled by default.

Promise.resolve/reject

Methods and are rarely needed in modern code, because syntax (we’ll cover it a bit later) makes them somewhat obsolete.

We cover them here for completeness and for those who can’t use for some reason.

creates a resolved promise with the result .

Same as:

The method is used for compatibility, when a function is expected to return a promise.

For example, the function below fetches a URL and remembers (caches) its content. For future calls with the same URL it immediately gets the previous content from cache, but uses to make a promise of it, so the returned value is always a promise:

We can write , because the function is guaranteed to return a promise. We can always use after . That’s the purpose of in the line .

creates a rejected promise with .

Same as:

In practice, this method is almost never used.

Practical JavaScript Promise example

We’ll show you how to load a JSON file from the server and display the message on a webpage.

Suppose that we have the following JSON file: https://www.javascripttutorial.net/sample/promise/api.json

Here are the contents of the api.json file:

The following shows the HTML page that contains a button. When you click the button, the page loads data from the JSON file and shows the message:

To load the JSON file, we’ll use the object. Later, you will learn how to use the method.

The following function returns a Promise object that loads data from a remote file:

Inside the executor, we called function and passed in the response if the HTTP status code is 200, otherwise, we invoked the function and passed in the HTTP status code.

Register the button click event listener and call the method on the Promise. If the load is successful, then we show the message returned from the server. Otherwise, we show the error message with the HTTP status code.

Promise.any

Similar to , but waits only for the first fulfilled promise and gets its result. If all of the given promises are rejected, then the returned promise is rejected with – a special error object that stores all promise errors in its property.

The syntax is:

For instance, here the result will be :

The first promise here was fastest, but it was rejected, so the second promise became the result. After the first fulfilled promise “wins the race”, all further results are ignored.

Here’s an example when all promises fail:

As you can see, error objects for failed promises are available in the property of the object.

Перевод функций в стиле Node.js на использование объектов Promise

В JavaScript не все асинхронные функции и библиотеки поддерживают объекты Promise изначально. Обычно типичные функции, основанные на обратных вызовах, требуется преобразовать так, чтобы они возвращали объекты Promise. Этот процесс называется переводом на использование объектов Promise.

К счастью, соглашения об обратных вызовах, используемые на платформе Node.js, позволяют создавать функции, способные переводить любые функции в стиле Node.js на использование объектов Promise. Это несложно осуществить с помощью конструктора объекта Promise. Создадим новую функцию promisify() и добавим ее в модуль utilities.js (чтобы ее можно было использовать в приложении веб­паука):

module.exports.promisify = function(callbackBasedApi) {
  return function promisified() {
    const args = [].slice.call(arguments);
    return new Promise((resolve, reject) => {  //
      args.push((err, result) => {             //
        if(err) {
          return reject(err);                  //
        }
        if(arguments.length <= 2) {            //
          resolve(result);
        } else {
          resolve([].slice.call(arguments, 1));
        }
     });
     callbackBasedApi.apply(null, args);      //
   });
 }
};

Приведенная выше функция возвращает другую функцию – promisifed(), которая является версией callbackBasedApi, возвращающей объект Promise. Вот как она работает:

  1. функция promisifed() создает новый объект с помощью конструктора Promise и немедленно возвращает его;
  2. в функции, что передается конструктору Promise, мы передаем специальную функцию обратного вызова для вызова из callbackBasedApi. Поскольку функция обратного вызова всегда передается в последнем аргументе, мы просто добавляем ее в список аргументов (args) функции promisifed();
  3. если специальная функция обратного вызова получит ошибку, объект Promise немедленно отклоняется;
  4. в случае отсутствия ошибки осуществляется разрешение объекта Promise со значением или массивом значений, в зависимости от количества результатов, переданных функции обратного вызова;
  5. в заключение вызывается callbackBasedApi с созданным списком аргументов.

Итоги

  1. Используйте промисы в ситуациях, когда вы работаете с асинхронным или блокирующим кодом.
  2. Для обработки ситуации успешного разрешения промиса используйте метод , для тех случаев, когда промис отклонён, применяйте .
  3. Используйте методы и во всех промисах.
  4. Если что-то нужно сделать и при разрешении, и при отклонении промиса, воспользуйтесь методом .
  5. Состояние промиса, после того, как он оказывается разрешённым или отклонённым, уже не меняется.
  6. К одному промису можно добавлять несколько обработчиков, объединяя их вызовы в цепочки.
  7. Все методы объекта , являются ли они статическими методами, или методами прототипа объекта, возвращают промисы.
  8. Метод не меняет порядок промисов в переданной ему структуре данных, он не привязывает его к очерёдности разрешения промисов.

Уважаемые читатели!

Итого

  • Промис – это специальный объект, который хранит своё состояние, текущий результат (если есть) и колбэки.
  • При создании автоматически запускается функция-аргумент, которая должна вызвать при успешном выполнении и – при ошибке.
  • Аргумент (только первый, остальные игнорируются) передаётся обработчикам на этом промисе.
  • Обработчики назначаются вызовом .
  • Для передачи результата от одного обработчика к другому используется чейнинг.

У промисов есть некоторые ограничения. В частности, стандарт не предусматривает какой-то метод для «отмены» промиса, хотя в ряде ситуаций (http-запросы) это было бы довольно удобно. Возможно, он появится в следующей версии стандарта JavaScript.

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector