Сроки при преобразовании обещания в асинхронные ожидают

1

У меня есть следующий блок

return this
        .configure(config => {
          viewModel.configure(config, this);
          return config;
        })
        .then(() => {
          this.activate();
        });

который эквивалентен следующему блоку, предложенному vscode auto promise-> преобразованием async/await:

await this.configure(config => {
  viewModel.configure(config, this);
  return config;
});
this.activate();

Мой вопрос в том, что они на самом деле одинаковы во времени? В примере с обещанием не должен быть второй fn в then() 1 микрозадаче от обратного вызова, который принимает config?

Дополнительный вопрос для ясности: следующие точки зрения в перспективе:

Promise.all(tasks)
  .then(() => {
    othertasks.forEach(() => doStuff());
  })
  .then(() => {
    prune();
  });

а также:

await Promise.all(tasks);
othertasks.forEach(() => doStuff();
await Promise.resolve();
prune();
  • 1
    Это полезно знать, но я чувствую, что также важно упомянуть: если ваше приложение зависит от порядка на уровне микрозадач, вы обычно делаете что-то не так.
Теги:
async-await
promise

2 ответа

1
Лучший ответ

Я думаю, что есть довольно путаница в том, что произошло с VSCode, о том, что вы задали как вопрос, и о том, какой ответ вы получили.

Я попытаюсь прояснить все моменты, как могу, надеясь, что у меня вопрос правильно.

Позвольте мне начать говорить, что...

Эти два блока не эквивалентны

Следующий фрагмент кода:

this
  .configure(config => {
    viewModel.configure(config, this);
    return config;
  })
  .then(value => 'some ${value}');

"эквивалентен" только этому:

await this
  .configure(config => {
    viewModel.configure(config, this);
    return config;
  })
  .then(value => 'some ${value}');

Это потому, что await имеет меньший приоритет, чем цепочка методов /then конкатенация.

(async function (){
  const then = await Promise.resolve(1).then(one => one + one);
  console.log(then); // this is number 2
  const something = await 123;
  console.log(something); // this is number 123
}());

Причина, по которой вы правильно смущены, заключается в том, что VSCode перехитрил ваши намерения.

  return this
    .configure(config => {
      viewModel.configure(config, this);
      // configure() returns a Promise
      // and config can be a Promise too
      return config;
    })
    .then(() => {
      // but you are not using here the config value
      // or, if it was a promise, whatever value it resolved
      // and you are also not returning any value
      this.activate();
    });

Так как VSCode знает, что configure можно использовать, и его возвращаемое значение также может быть обещанием, что подразумевает, что activate может произойти только после того, как config окончательно решена, он также знает, что extra тик не имеет смысла, потому что вам не нужно ничего config, либо как значение, либо обещание, так что activate может быть вызвана сразу.

Так как вы также не возвращали значения за последнее then, весь return можно отбросить.

// only async to wait for
await this.configure(config => {
  viewModel.configure(config, this);
  return config;
});
// with config either value or promise
// there nothing else to wait for, so
// let invoke activate without returning
// anything, producing is the same undefined result
this.activate();

Напомним, что происходит внутри этого:

(async function (){
  const config = new Promise(res => setTimeout(res, 1000));
  console.time('awaiting');
  const value = await Promise.resolve(1).then(() => {
    return config;
  });
  console.timeEnd('awaiting');
  // awaiting: 1000.XXXms
}());

Если вы случайно не используя возвращаемое значение внутри, что в прошлом then, вы бы видели, что VSCode не мог уронил, наиболее вероятно, переадресовали в const value = await...; this.activate(value); const value = await...; this.activate(value); который также все еще в порядке.


К предыдущему комментарию, заявив:

Для разрешения значения в простом выражении это:

await something

Это эквивалентно этому:

Promise.resolve(something).then()

Оба они приводят к ожидающемуся обещанию.

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

const resolved = await anything означает, что resolved всегда является ценностью, а не ожидающим обещания.

Это вполне возможно, что все await: он не перестанет ждать, пока не появится значение.

Пример:

(async function (){
  const something = Promise.resolve(Math.random());
  // this logs the random number as typeof number
  console.log(await something);
  // this also logs the random number as typeof number
  console.log(await Promise.resolve(something).then());
  // while this is one is the only pending promise
  console.log(Promise.resolve(something).then());
}());

Причина, по которой вы в итоге видите Ожидание обещания в консоли, заключается в том, что AIIFE (Асинхронное выражение, вызываемое сразу же) является обещанием, и вы можете его ожидать в другом месте.

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

(async function (){
  // instant return, it just fine
  // return 123;

  // return promise (unnecessary ticks added)
  return Promise.resolve(123).then();
}()).then(console.log);

В обоих случаях записывается номер 123.

Надеюсь, теперь ясно, специально для OP, что произошло в VSCode, и, особенно, почему это произошло.

С уважением.

  • 0
    Вы технически правы. Здесь был какой-то контекст, который я должен был прояснить раньше. Во всяком случае, ваш ответ гарантирует, что будущие читатели не могут быть введены в заблуждение из-за отсутствия контекста, так что спасибо за указание на это.
  • 0
    Спасибо за разъяснение. Вы, по сути, действительно объяснили то же, что я объяснил, и почему VSCode также удалил последний .then() так как это лишняя галочка. У меня не было контекста, иначе это помогло бы мне понять и, возможно, просто прокомментировать, чтобы прояснить этот пост. Извините за "заимствование" ответа тогда :-)
Показать ещё 1 комментарий
2

EDIT: Я должен уточнить, что касается ответа Андреа Джаммархи, что мой ответ был сугубо и связан только с разницей в количестве тиков между синхронно исполняемым кодом, а не тем, является ли результат написанного кода фактически эквивалентным ( очевидно, что не ожидаемые обещания дадут обещания, тогда как ожидаемые обещания дадут значения)

Это имело бы большее значение в контексте того, что я и bigopon обсуждали проблему github, где он принимал предложение VS Code, чтобы удалить "избыточный". .then из цепочки обещаний в фрагменте устаревшего кода, который оказывается чувствительным к тонким вопросы времени.

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

Тогда было обсуждено:

somePromise.then(() => {
   ...
}).then(() => {
   doStuff();
})

Были бы такие же тайминги, как это:

await somePromise;
doStuff();

На мой ответ был: no, doStuff() во втором фрагменте будет выполнять один тик раньше.

Когда кто - то предложил, что await или .then будет фактически выполняться синхронно, если переданное обещание было уже решено, что побудило меня написать этот ответ и пояснить, почему нет.

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

Оригинальный ответ

Пример 1

Для разрешения значения в простом выражении это:

await something

Это эквивалентно этому:

Promise.resolve(something).then()

Оба они приводят к ожидающемуся обещанию.

Пример 2.

Для постановки задачи в очередь:

await Promise.resolve();
doStuff();

Это эквивалентно этому:

Promise.resolve().then(() => {
    doStuff();
})

В обоих случаях doStuff() происходит на следующем тике.

Для того, чтобы определить, является ли регулярное .then эквивалентно серии цепи await s, вам просто нужно рассчитывать .then и await. Если каждый из них является одним и тем же между двумя указанными фрагментами кода, тогда количество времени/тиков/все, проходящих между этими фрагментами кода, будет одинаковым.

Пример 3.

Другой пример:

await Promise.resolve();
doStuff();
await Promise.resolve();
doStuff();
await Promise.resolve();
await Promise.resolve();
doStuff();

Это эквивалентно этому:

Promise.resolve()
.then(() => {
    doStuff();
})
.then(() => {
    doStuff();
})
.then(() => {})
.then(() => {
    doStuff();
})

Обратите внимание, что сам Promise.resolve() не влияет на тайминги. Он возвращает обещание. Он then()/await что превращает его в ожидающий.

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

Что говорит спецификация

  1. Если обещание. [[PromiseState]] находится в ожидании, тогда

    а. Добавить executeReaction как последний элемент списка, который является обещанием. [[PromiseFulfillReactions]].

    б. Добавить rejectReaction как последний элемент списка, который является обещанием. [[PromiseRejectReactions]].

  2. Иначе, если обещание. [[PromiseState]] "выполнено", тогда

    а. Пусть значение будет обещано. [[PromiseResult]].

    б. Выполните EnqueueJob ("PromiseJobs", PromiseReactionJob, "performReaction, value").

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

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

  • 1
    Перечитав, я согласен. OP, вы можете отменить выбор моего ответа? Я считаю, что это не правильно.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню