2017-02-03 3 views
1

Для кода нижеResolve порядок Promises в Promises

function inner() { 
    new Promise(function(resolve,reject){ 
    resolve() 
    }).then(function(){ 
    console.log('Inner Promise') 
    }) 
} 
function outer() { 
    return new Promise(function(resolve, reject){ 
    resolve() 
    inner() 
    }) 
} 

outer().then(function(data) { 
    console.log('Outer Promise') 
}) 

Выхода

Inner Promise 
Outer Promise 

Я думал, что внешняя решительность будет первой, чтобы войти в очередь сообщений JS с последующим внутренней решимостью , Однако JS Event Loop сначала запускает внутреннее разрешение, а затем Outer.

Что обещает решить внутренне?

+1

Как вы закодировали это, 'inner()' является осиротевшей ветвью обещаний. Вы знаете только, когда он запущен, но он ничем не согласован ни с чем. Обычно это неправильный способ кодирования обещаний и даже предупреждение в некоторых библиотеках обещаний, потому что это часто ошибка. Обычно вы возвращаете 'inner()' обещание из 'outer()' и не должны 'external()' создавать свои собственные обещания. Тогда все будет скоординировано. – jfriend00

+3

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

ответ

6

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

Что обещает решить внутренне?

resolve() изменяет внутреннее состояние обещания на Выполнено. В этот момент, если есть какие-либо .then() обработчики, уже прикрепленные к обещанию, они добавляются в очередь, которая будет выполняться, когда расчистка стека и текущий пробег Javascript завершается, и возвращает управление обратно в систему. Обратите внимание, как вы увидите в этом случае (когда вы читаете пошаговый анализ ниже), если еще нет зарегистрированных .then() обработчиков, в очередь еще ничего не добавлено.

Я думал, что внешнее решение будет первым, чтобы войти в сообщение JS Очередь, за которой следует внутреннее решение. Однако JS Event Loop запускает Внутреннее разрешение сначала, а затем следует разрешение Outer.

Действия по обещанию не добавлены в очередь. resolve() является синхронным. Он немедленно меняет состояние текущего обещания на заполненное состояние. Если в то время, когда обещание будет разрешено, есть уже зарегистрированные .then() обработчики, то они являются тем, что добавлено в очередь. Но в обоих ваших обещаниях, в настоящий момент каждое ваше обещание разрешено, в настоящее время нет обработчиков .then(). Таким образом, эти обработчики .then() не будут поставлены в очередь в точке, где обещание будет разрешено. Вместо этого они будут поставлены в очередь позже, когда метод .then() действительно запускается и регистрирует их.

Вот немного анализа того, как работает ваш код и вероятное объяснение:

  1. Сначала вы называете outer(). Это создает объект Promise и синхронно вызывает обратный вызов исполнителя обещания, который вы передаете ему.
  2. Этот обратный вызов вызывает resolve(), который будет стоять в очереди на вызов всех подключенных в данный момент обработчиков .then(). Обратите внимание, что на данный момент вы звоните resolve(), пока нет обработчиков .then(), потому что в этом коде outer().then() вы все еще работаете outer() и .then() после того, как он еще не запущен, так что пока еще нет очереди, чтобы стоять в очереди (это, вероятно, ключ к заказу, который вы наблюдаете, - читайте дальше для получения дополнительной информации).
  3. Затем код вызывает inner(). Это создает новое обещание, а затем (по-прежнему работает синхронно) вызывает вызов отправителя обещания, который вы передаете там, который вызывает resolve(). Опять же, до сих пор нет подключенных обработчиков .then(), поэтому еще не запланировано на будущее выполнение.
  4. Теперь исполнитель Promise внутри inner() возвращается, и метод .then() вызывается в этом обещании внутри inner(). Это обещание уже было разрешено, поэтому, когда этот обработчик .then() вызывается, обещание знает, чтобы запланировать его запуск в будущем. Поскольку все обработчики .then() называются асинхронно, когда стек разматывает только код платформы, он не запускается немедленно, но он планируется запустить в будущем, отправив его в очередь. Реализация зависит от того, как работает эта очередь (макрозадача или микрозадача и т. Д.), Но мы знаем, что она гарантируется спецификацией Promise для запуска после текущей синхронной части JS-кода, который выполняет завершение работы и возвращает управление обратно в систему.
  5. Теперь inner() возвращается (код по-прежнему работает синхронно).
  6. В настоящее время outer() возвращает и .then() метод в outer().then() работает. Как и в предыдущем примере, когда вызывается этот метод .then(), обещание узла уже разрешено. Таким образом, механизм обещания запланирует обратный вызов обработчика .then() для запуска, добавив его в очередь.
  7. Если эти два .then() обработчики в пунктах 4 и 6 находятся в очереди в порядке их выполнения (что было бы логично реализации), то вы бы увидели .then() обработчик на inner() запуска первого, а затем .then() обработчик на outer() будет работать начиная с inner().then() ran first before external(). then() `. Это то, что вы наблюдаете.
  8. Несмотря на то, что outer() разрешен до inner(), на момент разрешения outer() не было обработчиков .then(), поэтому нет необходимости планировать его в будущем при его разрешении. Вероятно, поэтому, хотя он сначала разрешен, его обработчики .then() не запускаются первыми. После того, как разрешены как inner(), так и outer(), это первый метод inner, поэтому он получает первую трещину при планировании обработчика .then(), и это то, что вы наблюдаете.

Вы можете получить дополнительный контекст для того, что происходит, читая и изучая эти ссылки:

What is the order of execution in javascript promises

Difference between microtask and macrotask within an event loop context.


Если вы хотите, чтобы более четко указать, что внутренний .then() обработчик будет стрелять первым, вы можете просто приковать его к outer() обещание, как это:

function inner() { 
 
    return new Promise(function(resolve,reject){ 
 
    resolve(); 
 
    }).then(function(){ 
 
    console.log('Inner Promise') 
 
    }) 
 
} 
 
function outer() { 
 
    // Add return here to chain the inner promise 
 
    // make to make sure that outer() does not resolve until 
 
    // inner() is completely done 
 
    return inner(); 
 
} 
 

 
outer().then(function(data) { 
 
    console.log('Outer Promise') 
 
})

Если вы хотел гарантировать, что обработчик outer().then() был вызван первым, вам нужно будет выбрать другую структуру, поскольку эта структура не заставляет этот тип порядка в любом wa y и не может быть увязена в этом направлении, если вы сознательно не задерживаете работу inner() (используя setTimeout() или какую-то такую ​​вещь) или реструктурируйте код. Например, если вы действительно хотели перестроить, чтобы заставить inner() запустить в прошлом, вы бы пнуть его в outer().then() обработчика, как это:

function inner() { 
 
    return new Promise(function(resolve,reject){ 
 
    resolve() 
 
    }).then(function(){ 
 
    console.log('Inner Promise') 
 
    }) 
 
} 
 
function outer() { 
 
    return new Promise(function(resolve, reject){ 
 
    resolve() 
 
    }) 
 
} 
 

 
outer().then(function(data) { 
 
    console.log('Outer Promise') 
 
    return inner(); 
 
})

+0

Добавлены примеры кода и дополнительные пояснения. – jfriend00

1

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

Да, «внешнее» обещание разрешено первым. Поместите console.log рядом с вызовом resolve.
Но нет, внешний обратный вызов сначала не помещается в очередь, поскольку он устанавливается после внутреннего, а затем обратного вызова. То, что вы делаете, по существу, эквивалентно

var outer = Promise.resolve(); 
var inner = Promise.resolve(); 
inner.then(function() { 
    console.log('Inner Promise') 
}); 
outer.then(function(data) { 
    console.log('Outer Promise') 
}); 

но запутывается из-за вложенности (синхронно) вызовы функций.