Несколько цепочек на JS Promise? - PullRequest
0 голосов
/ 25 августа 2018

Сегодня я попробовал, что произойдет, когда я добавлю несколько цепочек к Обещанию. Мой тестовый код выглядит так (TypeScript)

class Test {

    private simulateHeavyStuff(time: number){
        // console.log("-"+time+"-");
        var start = new Date().getTime();
        var end = start;
        while(end < start + time) {
            end = new Date().getTime();
        }
    }

    test(): Promise<void> {
        let promise = new Promise<void>((resolve, reject) => {
            resolve();
        });

        // ##### ADDING CHAIN 1 #####
        promise
        .then(()=>console.log("then1"))
        .then(()=>this.simulateHeavyStuff(2000))
        .then(()=>console.log("then11"))
        .catch(console.error)

        return promise;
    }

}

let t = new Test();

// ##### ADDING CHAIN 2 #####
t.test()
.then(()=>console.log("then2"))
.then(()=>console.log("then21"))
.catch(console.error)

Это распечатывается:

then1
then2
        <---it's pausing here
then21
then11

Может ли кто-нибудь объяснить детерминизм, стоящий за этим поведением?

Я думал, что это будет печатать (параллельные цепочки)

then1
then2
then21
       <--- I guessed the pause will be here
then11

или (одна цепь)

then1
       <--- with the pause here
then11
then2
then21

Ответы [ 3 ]

0 голосов
/ 25 августа 2018

Это потому, что ваш метод test возвращает переменную promise, которая не совпадает с результатом, возвращаемым catch, поэтому при вызове .test() он не будет ожидать всю цепочку внутри test.

Если вы измените

promise
    .then(()=>console.log("then1"))
    .then(()=>this.simulateHeavyStuff(2000))
    .then(()=>console.log("then11"))
    .catch(console.error)

до

promise = promise
    .then(()=>console.log("then1"))
    .then(()=>this.simulateHeavyStuff(2000))
    .then(()=>console.log("then11"))
    .catch(console.error)

это будет работать так, как вы ожидали.

0 голосов
/ 25 августа 2018

Ответ, по-видимому, заключается в том, что вы - возможно по ошибке - создали два обещания (поскольку .then создает новый объект обещания на основе предыдущего, см. MDN ).Посмотрите на комментарии ниже:

test(): Promise<void> {
  // this creates the base promise "A"
  let promise = new Promise((resolve, reject) => {
        resolve();
    });
  // this creates promise "A1" by adding a chain of operations to "A"
  // you are not returning it however
  promise
    .then(()=>console.log("then11"))
    .then(()=>console.log("then12"))
    .then(()=>this.simulateHeavyStuff(2000))
    .then(()=>console.log("then14"))
    .catch(console.error)

  // this returns "A" (not "A1")
    return promise;

}



// this creates promise "A2"
t.test()
  .then(()=>console.log("then2"))
  .then(()=>console.log("then21"))
  .catch(console.error)

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

«А1» -> «А2» -> «А1» ...

class Test {

    simulateHeavyStuff(time){
        // console.log("-"+time+"-");
        var start = new Date().getTime();
        var end = start;
        while(end < start + time) {
            end = new Date().getTime();
        }
        console.log('then13 (heavy stuff)');
    }

    test() {
        let promise = new Promise((resolve, reject) => {
            resolve();
        });

        // ##### ADDING CHAIN 1 #####
        promise
        .then(()=>console.log("then11"))
        .then(()=>console.log("then12"))
        .then(()=>this.simulateHeavyStuff(2000))
        .then(()=>console.log("then14"))
        .catch(console.error)
        
        return promise;

    }

}

let t = new Test();

// ##### ADDING CHAIN 2 #####
t.test()
.then(()=>console.log('then21'))
.then(()=>console.log('then22'))
.then(()=>console.log('then23'))
.catch(console.error)

В вашем случае - когда все шаги выполнения полностью синхронизированы - программа выполняется детерминистически: т. Е. i -ая цепочка .thenоперация обещания «А1» предшествует i -ой цепочке .then операции «А2»;и i + 1 операция обещания "A1" следует за i -й цепочкой .then операция обещания "A2".

Спецификация ECMA Script 2015 , кажется, подтверждает это поведение, поскольку .then пулы должны стоять в очереди в очереди заданий.

0 голосов
/ 25 августа 2018

ОК, наверное, я понимаю, что происходит после некоторых разъяснений Jaromanda X в комментариях!

Это полностью детерминировано!

Обещания добавляют обратные вызовы длятак называемый стек Microtasks .Помимо других стеков JS, эти Микрозадачи выполняются, когда стек JS пуст (весь синхронный код выполнен) ... см. Видео, опубликованное Jaromanda X в комментариях!

Так это происходит так:

  • «then1» добавляется в стек MT
  • test () возвращает
  • «then2» добавляется в стек MT

Синхронныйкод готов!Время микрозадач!

Стек MT теперь выглядит следующим образом

then1
then2

Итак, запускается then1 и добавляет еще одну Microtask в стек MT, когда он возвращает Обещание.Стек MT теперь выглядит так:

then2
heavyStuff(2000)

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

...