Ждите тогда объект - PullRequest
0 голосов
/ 26 мая 2020

Я пытаюсь записать logi c в JS, чтобы внезапно остановить выполнение функции (без дальнейшего выполнения) в последовательности вызовов. Проверьте код ниже. Почему не останавливается после второго звонка?

function myprom (value) {
  return new Promise (function (resolve, reject) {
    resolve("I Promise " + value)
  })
}

function myfunc (value) {
  console.log("Starting in " + value)
}

function terminate() {
  setTimeout(() => console.log("Edning"), 1)
}

function dummy() {
  console.log("I am not supposed to print anything")
}

async function call() {
  await myprom("to end without showing next line")
    .then(result => {
      myfunc(1)                       // Call # 1
      console.log(result)             // Call # 2
      terminate()                     // Call # 3
      dummy(1)                        // Call # 4
      terminate()                     // Call # 5
    })
    .catch(err => {
      console.log(err)
    })
}

call()

Ниже приведен фактический результат моего кода.

Starting in 1
I Promise to end without showing next line
I am not supposed to print anything
Ending
Ending

Ожидается,

Starting in 1
I Promise to end without showing next line
Ending

В общем. как остановить выполнение js внезапно внутри объекта .then?

Ответы [ 3 ]

0 голосов
/ 26 мая 2020

Когда вы вызываете этот terminate(), который является следующим:

function terminate() {
  setTimeout(() => console.log("Edning"), 1)
}

setTimeout() подталкивает его обратный вызов к будущему циклу события l oop и ВСЕГО остального в текущем цикле событие l oop запускается перед ним. Только когда текущая цепочка кода завершится и управление вернется обратно к событию l oop, setTimeout() получит шанс запустить свой обратный вызов.

Итак, вот последовательность того, что происходит в вашем код:

  1. call() выполняется.
  2. myprom() выполняется. Это немедленно выполняет свое обещание, поэтому он планирует запуск своих обработчиков .then(), как только управление вернется к событию l oop.
  3. .then() и .catch() run. Все, что они делают в этот момент, это регистрируют некоторые обратные вызовы, которые будут вызваны позже, когда обещание будет готово для их выполнения.
  4. Ваша функция asyn c возвращается, и управление возвращается к событию l oop.
  5. Уже обработанное обещание является первым в строке в событии l oop, поэтому оно получает управление и выполняет свой .then() обработчик.
  6. myfunc(1) запускается и выводит Starting in 1
  7. console.log(result) запускается и выводит I Promise to end without showing next line
  8. terminate() запускается, и это устанавливает таймер на 1 мс с этого момента. Эта функция возвращается, и таймер запускается по расписанию для будущего тика события l oop, когда прошло 1 мс. Вызывается
  9. dummy(1), и выводится I am not supposed to print anything.
  10. terminate() вызывается снова, и это снова планирует запуск другого таймера через 1 мс в будущем тике события l oop.
  11. await обрабатывает обещание и разрешает обещание, которое async функция call() возвращает. await не делает здесь ничего полезного, и нечего ожидать от обещания, что call() тоже вернет.
  12. Управление возвращается обратно событию l oop.
  13. Первый таймер, который вы установили при первом вызове terminate(), запускается, и он выводит. Это возвращает управление событию l oop.
  14. Второй таймер, который вы установили при втором вызове terminate(), запускается и запускается. Это возвращает управление событию l oop, и все, что связано с этим кодом, выполняется.

В общем. как внезапно остановить выполнение js внутри объекта .then?

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

async function call() {
  await myprom("to end without showing next line")
    .then(result => {
      myfunc(1)                       // Call # 1
      console.log(result)             // Call # 2
      if (result === "I Promise to end without showing next line") {
          // skip the rest of the code in this callback
          return;
      }
      terminate()                     // Call # 3
      dummy(1)                        // Call # 4
      terminate()                     // Call # 5
    })
    .catch(err => {
      console.log(err)
    })
}

call()

Поскольку вы находитесь в обработчике .then(), вы также можете сделать throw someError, и это внезапно остановит обработчик .then(), как и бросить любой исключение прерывает выполнение текущей функции. Поскольку это находится в обработчике .then(), это исключение будет немедленно перехвачено, а затем выполнится обработчик .catch() и передаст ему созданное вами исключение. Обычно это не используется для обычного пути выполнения, потому что это чаще указывает на некоторую ошибку. Обычный путь выполнения, скорее всего, отловит условие локально и использует ветвление или if/else logi c или что-то в этом роде, чтобы решить, какой код в функции будет выполняться следующим.


Обратите внимание, если вы ' вы уже запустили некоторые таймеры, а затем, чтобы они не срабатывали, вам нужно сохранить timerID, который возвращается setTimeout(), и использовать clearTimeout() на этом timerID, чтобы отменить таймер.

Если вы действительно хотите резкую остановку, чтобы остановить весь процесс, тогда в node.js вы также можете позвонить process.exit().


Непонятно, почему вы подумали, что вывод из dummy(1) и второй вызов terminate() не будет отображаться на выходе? Вы позвонили по номеру dummy(1). Ничто в функции terminate() не заставляет Javascript пропускать остальную часть кода в обработчике .then(). Итак, он продолжает и выполняет как dummy(1), так и второй terminate(), и вы видите результат обоих.

0 голосов
/ 26 мая 2020

Функция terminate не останавливает выполнение функции, а вместо этого планирует выполнение функции позже с таймаутом не менее 1 секунды. Поэтому вместо этого функцию следует называть delay. Я добавил реализацию того же самого на основе Promise:

function delay(time = 1) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Ending');
      resolve();
    }, time);
  })
}

Может ожидаться функция, возвращающая скалярное значение. Таким образом, нет необходимости заключать myProm в конструктор Promise. Предполагая, что myprom является реальной асинхронной операцией c вместо функции, возвращающей строку, конечно.

function myprom (value) {
  return `I Promise ${value}`;
}

При использовании функций asyn c обратный вызов с .then и .catch не является обязательным.

async function call() {
  try {
    const result = await myprom("to end without showing next line")
    console.log(result)
    myfunc(1)
  } catch (e) {
    console.error(e);
  }
}

Существующий фрагмент кода с изменениями будет выглядеть так:

// scalar values can be resolved too
function myprom (value) {
  return `I Promise ${value}`;
}

function myfunc (value) {
  console.log("Starting in " + value)
}

function delay(time = 1) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Ending');
      resolve();
    }, time);
  })
}


function dummy() {
  console.log("I am not supposed to print anything")
}

async function call() {
  try {
    const result = await myprom("to end without showing next line")
    console.log(result)
    myfunc(1)
    await delay();
    dummy(1);
    await delay();
  } catch (e) {
    console.error(e);
  }
  
  return undefined;
}

call();

Однако, как объяснялось, это не остановит поток выполнения:

  • myprom работает

  • result печатается

  • myFunc выполняется

  • с задержкой в ​​1 секунду, сообщение Ending печатает (поскольку событие l oop не блокируется многими операциями asyn c)

  • фиктивное выполняется

  • после задержки Через 1 секунду сообщение Ending печатает

  • , функция возвращает Promise, которое разрешается до значения undefined.

Вместо использования delay, мы можем использовать return, когда нам нужно прекратить выполнение функции:

function myprom(value) {
  return `I Promise ${value}`;
}

function myfunc(value) {
  console.log("Starting in " + value)
}


async function call() {
  try {
    const result = await myprom("to end without showing next line")
    console.log(result)
    myfunc(1);
    return
  } catch (e) {
    console.error(e);
  }
}

call();
0 голосов
/ 26 мая 2020

Вместо вызова terminate вы можете использовать return. Это решит вашу задачу.

...