Работа промисов

382
14 февраля 2017, 21:24

Добрый Вечер. Наткнулся на такой пример с промисами :

var promise = new Promise(function(resolve, reject) {
    resolve(1);
});
function logger(message) {
    console.log(message);
}
promise
  .then(logger)
  .then(logger(2))
  .then(() => logger(3));

В результате на выходе результат - 2, 1, 3. Правильно ли я понимаю, что перед тем, как начинать выполнять данный код, с начала отдается приоритет вызову функции logger ? Именно поэтому в результате с начала идет 2 ? Функция logger, возвращает undefined, а в консоль пишет 2. Затем, после выполнения промиса, после первого then (который вернул вместо промиса undefined), в консоль запишется 1, а уж затем вернется 3.

Answer 1

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

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

Ну да к делу.

Вывод у вас происходит при вызове функции logger. Посмотрим, когда она вызывается.

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

promise
  .then(logger)
  .then(logger(2))
  .then(() => logger(3));
  • В первом вызове then аргументы разрешаются в значения напрямую, без вычислений. Никаких вызовов logger там не происходит. Пока что.
  • Во втором вызове then аргумент уже выражение, требующее вычисления перед передачей: вызов функции logger. Тут-то и вызывается logger(2) и выводится 2. А возвращается из logger... ничего, что снаружи видно как undefined, которое функцией не является а потому приводит к "сквозному пробрасыванию" результата в следующий then (если таковой, как здесь, имеется), поскольку:

    If the first argument is omitted or provided a non-function, the new Promise that is created simply adopts the fulfillment state of the Promise that then is called on (if it becomes fulfilled).

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

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then

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

  • В третьем вызове аргумент является функциональным выражением (стрелочной функцией, если точно), но для его вычисления вызовы logger не нужны, т. к. его результатом является функция, которая при вызове (но не создании!) вызывает logger и выводит что-то в консоль. А на этом этапе эта функция лишь создалась.

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

  • Сначала вызывается функция из конструктора Promise. Она приводит к разрешению промиса со значением 1.
  • Затем вызывается logger с аргументом, полученным из прошлого промиса: получается logger(1). А потому происходит вывод 1, а промис разрешается со значением undefined (т. к. функция ничего не вернула).

    When a value is simply returned from within a then lambda, it will effectively return Promise.resolve(<value returned by whichever handler was called>).

  • Затем сталкиваемся с пустым промисом, образованным без функции... фиг знает, что там внутри ещё происходит, но результат прошлого разрешения промиса (undefined) просто считается результатом и у этого и передаётся далее, состояние промиса (разрешился) при этом сохраняется.

  • Затем функция () => logger(3) вызывается с аргументом undefined, но JS наплевать на лишние аргументы и в результате успешно выполняется тело функции, выполняется logger(3) и выводится 3.
READ ALSO
Как обновить в контроллере изменение в фабрике?

Как обновить в контроллере изменение в фабрике?

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

321
Изменение документа в mongodb по времени

Изменение документа в mongodb по времени

В MongoDB можно создать индекс для удаления документа по времени (TTL)Имеется ли подобные средства для обновления документа

335
Как ускорить обработку данных JSON в JS?

Как ускорить обработку данных JSON в JS?

Необходимо уведомить Вас сразу: для меня эта тема новая, я знаком лишь с php

285