Как понять этот порядок выполнения Promise? - PullRequest
4 голосов
/ 07 октября 2019

Я не понимаю, почему этот кусок кода приводит к такому порядку? Кто-нибудь может уточнить это? Я думал, что Promises похожи на очередь FIFO, но вложенные функции Promise кажутся немного непредсказуемыми, или, может быть, используется какая-то другая структура данных?

new Promise(resolve => {
    resolve()
  })
  .then(() => {
    new Promise(resolve => {
        resolve()
      })
      .then(() => {
        console.log(1)
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1)
    new Promise((resolve => {
        resolve()
      }))
      .then(() => {
        new Promise(resolve => {
            resolve()
          })
          .then(() => {
            console.log(4)
          })
          .then(() => {
            console.log(6)
          })
      }).then(() => {
        console.log(5)
      })
  }).then(() => {
    console.log(3)
  })
console.log(0)

Вывод:

0
1
1.1
2
3
3.1
4
5
6

Ответы [ 3 ]

7 голосов
/ 07 октября 2019

Обещания асинхронны. Это означает, что каждый раз, когда вы создаете новое обещание, начинается новая асинхронная операция.

Что такое асинхронная операция в JS? Прежде всего вы должны понимать, что JS работает на однопоточном независимо от того, что вы делаете. Итак, чтобы сделать его похожим на асинхронный - есть нечто, называемое «цикл обработки событий» (взяло ссылку от комментария к оригинальному сообщению, tnx @Taki - отличный источник).

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

Таким образом, в принципе, здесь нет очереди «FIFO» - порядок асинхронных функций буквально зависит от таких вещей, как скорость вашего процессора, ваша операционная система и т. Д.

НО - есть способчтобы убедиться, что одно асинхронное действие выполняется только после завершения другого, это условие .then. Дело в том, что он только гарантирует, что определенная функция внутри .then будет выполнена после конкретного обещания, к которому он был присоединен, но ничего не говорит о порядке его выполнения в отношении других асинхронных операций (обещаний) в событии. петля. Например, в вашем коде:

new Promise(resolve => {
    resolve() // PROMISE A
  })
  .then(() => {
    new Promise(resolve => {
        resolve() // PROMISE B
      })
      .then(() => {
        console.log(1) //PROMISE C
      })
      .then(() => {
        console.log(2)
      })
      .then(() => {
        console.log(3.1)
      })
  })
  .then(() => {
    console.log(1.1) // PROMISE D
    new Promise((resolve => {
        resolve()
      }))

Я принял участие, чтобы объяснить:

Итак, Обещание А разрешается первым. это гарантирует, что обещание B разрешится сейчас. вот когда все усложняется: поскольку обещание B разрешено, оба обещания C и D теперь попадают в цикл обработки событий! Почему? потому что у Promise A было 2 .then предложения, поэтому, когда первый цикл завершается, событие принимает второй, который является обещанием D., но первое предложение .then имело также свое собственное предложение .then - обещание C, котороетакже входит в цикл обработки событий.

НЕТ НИКАКОГО СОЕДИНЕНИЯ МЕЖДУ ОБЕЩАНИЕМ D И C ! Они могут быть выполнены в любом порядке. сохраните эту логику, и вы увидите, как это работает для остальных обещаний, а также, если вы попытаетесь запустить его на другой ОС, возможно, порядок обещаний будет другим из-за разных реализаций ОС для цикла обработки событий. ,

Надеюсь, это поможет вам немного понять.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ : У меня мало опыта в JS, но обещания действительно заинтриговали меня, поэтому ясделал глубокое исследование об этом. Я стою за всем, что я здесь написал, но если есть какие-то исправления в моем объяснении, я хотел бы услышать!

РЕДАКТИРОВАТЬ

Ответ ниже менятакже правильно, но без объяснения, поэтому позвольте мне добавить к нему: если вы не возвращаете ничего внутри обещания (или предложение .then, которое также возвращает обещание), оно неявно возвращает разрешенное обещание без значения перед выполнениемВне обещания, например, добавление return new Promise.resolve() после console.log в обещание C, например. Когда все выполнено следующим образом, все предложения .then, следующие за обещанием B, войдут в цикл обработки событий только после окончания предыдущего (например, b заканчивается, поэтому C входит в цикл, затем следующий .then и т. Д.), Номежду ними могут входить и другие обещания или .then предложения (например, обещание D).

Но когда вы ВОЗВРАЩАЕТЕ обещание с цепочками .then, связанными с ним - это гарантирует, что весь блок предложений обещания + затем переходит в цикл обработки событий по одному, поэтому .thenпункты также будут выполняться в нужном вам порядке:)

tnx @Eugene Sunic для дополнения!

4 голосов
/ 07 октября 2019

Это приводит к непредсказуемому порядку из-за несуществующих возвратов в вашем коде.

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

Сначала выводится синхронный 0, затем выполняется весь первый блок обещаний, как вы сказали FIFO.

1,2, 3.1

После этого выполняется цепочка символов 1.1. После этого печатается блок 4,6

, следующий за цепочкой, который выводит 5 и, наконец,, последний выводимый на печать номер 3

Оставив нам 0,1,2, 3,1, 1,1, 4,6,5,3

new Promise(resolve => resolve())
  .then(() => {
    return new Promise(resolve => resolve())
      .then(() => console.log(1))
      .then(() => console.log(2))
      .then(() => console.log(3.1));
  })
  .then(() => {
    console.log(1.1);
    return new Promise((resolve => resolve()))
      .then(() => {
        return new Promise((resolve) => resolve())
          .then(() => console.log(4))
          .then(() => console.log(6))
      }).then(() => console.log(5))
  }).then(() => console.log(3))

console.log(0)
0 голосов
/ 07 октября 2019

Это FIFO, и выполнение выглядит следующим образом:

main [4] logs: 0 // main code executed, one executor added to FIFO (4)
4 [8,18] // executor at line 4 runs, two executors added to FIFO (8, 18)
8 [18,11] logs: 1 // etc etc
18 [11,23,36] logs: 1.1
11 [23,36,14] logs: 2
23 [36,14,27,33] 
36 [14,27,33] logs: 3
14 [27,33] logs: 3.1
27 [33,30] logs: 4
33 [30] logs: 5
30 logs: 6

, поскольку вы можете видеть его первым в порядке следования: [4,8,18,11,23,36,14,27,33,30], но в нем хранятся исполнители (обратные вызовы для обещаний, которые были выполнены или отклонены),не обещания. Другими словами: время, когда обещание выполнено или отклонено, зависит от того, будет ли оно добавлено в FIFO, а не от того, когда оно создано.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...