【问题标题】:Await in then object在 then 对象中等待
【发布时间】:2020-09-12 19:05:06
【问题描述】:

我正在尝试在 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

一般。如何在 .then 对象中突然停止 js 执行?

【问题讨论】:

  • 运行您的代码是个好主意:我漂亮确定这不是输出,因为您的代码中有一个类型为“Edning”;) (这不是修复该类型的提示,而是提示再次实际运行您的代码,以查看您在帖子中发布的内容是否仍然符合您的想法)
  • 话虽如此:为什么你期望你说你期望什么?您编写的代码中没有任何内容可以缩短 myprom.then... 代码的执行时间,那么您为什么认为它不应该运行您的其余代码呢?
  • 一种方法是throw一个错误,它会触发catch()
  • .then(myFunc).then(terminate).then(dummy) 不会在terminate 拒绝时调用dummy

标签: javascript html node.js web


【解决方案1】:

函数terminate 不会停止函数的执行,而是安排函数稍后运行,超时时间至少为 1 秒。因此,该函数应该被称为delay。我添加了一个基于Promise 的相同实现:

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

可以等待返回标量值的函数。因此,无需使用 Promise 构造函数包装 myProm。当然,假设myprom 是真正的异步操作而不是返回字符串的函数。

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

使用异步函数时,不需要使用.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);
  }
}

带有更改的现有代码 sn-p 如下所示:

// 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 的消息(因为事件循环没有被许多异步操作阻塞)

  • 虚拟执行

  • 延迟 1 秒后,将打印一条消息 Ending

  • 该函数返回一个解析为 undefined 值的 Promise。

当我们需要终止函数的执行时,我们可以使用return来代替delay

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();

【讨论】:

    【解决方案2】:

    当你打电话给terminate() 时:

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

    setTimeout() 将它的回调推送到事件循环的未来循环,并且事件循环的当前循环中的所有其他内容都在它之前运行。只有当当前的代码链完成并且控制权返回到事件循环时,setTimeout() 才有机会运行它的回调。

    所以,下面是代码中发生的顺序:

    1. call() 被执行。
    2. myprom() 被执行。这会立即解决它的承诺,因此它会安排它的 .then() 处理程序在控制返回事件循环后立即运行。
    3. .then().catch() 运行。他们此时所做的只是注册一些回调,以便稍后在 Promise 准备好执行它们时调用。
    4. 您的异步函数返回并且控制返回到事件循环。
    5. 已解决的承诺在事件循环中排在第一位,因此它可以控制并执行它的 .then() 处理程序。
    6. myfunc(1) 运行并输出 Starting in 1
    7. console.log(result) 运行并输出 I Promise to end without showing next line
    8. terminate() 运行并从现在开始设置一个 1 毫秒的计时器。该函数返回,并且计时器计划在 1 毫秒过去后在事件循环的未来滴答声中运行。
    9. dummy(1) 被调用并输出 I am not supposed to print anything
    10. 再次调用 terminate() 并再次安排另一个计时器在事件循环的未来滴答声中以 1 毫秒运行。
    11. await 处理承诺并解析async 函数call() 返回的承诺。 await 在这里没有任何用处,也没有任何东西监听call() 返回的承诺。
    12. 控制返回给事件循环。
    13. 您在第一次调用terminate() 时设置的第一个计时器开始运行并输出。这会将控制权返回给事件循环。
    14. 您在第二次调用terminate() 时设置的第二个计时器开始运行,然后它就放了。这会将控制权返回给事件循环,并且与此代码相关的所有内容都已完成。

    一般。如何在 .then 对象中突然停止 js 执行?

    如果您要问的是如何跳过连续调用的其他函数,那么您通常会使用条件语句在 .then() 回调的早期分支 return。例如,您可以这样做:

    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 逻辑或类似的东西来决定函数中的哪些代码接下来执行。


    注意,如果您已经启动了一些计时器,那么为了防止它们触发,您必须保存 setTimeout() 返回的 timerID 并在该 timerID 上使用 clearTimeout() 来取消计时器。

    如果你真的想突然停止来停止你的整个进程,那么在node.js中,你也可以调用process.exit()


    不清楚为什么您认为dummy(1) 的输出和对terminate() 的第二次调用不会显示在输出中?你打电话给dummy(1)terminate() 函数中的任何内容都不会导致 Javascript 跳过 .then() 处理程序中的其余代码。因此,它继续并执行dummy(1) 和第二个terminate(),因此您会看到两者的输出。

    【讨论】:

      【解决方案3】:

      您可以使用 return 而不是调用终止。这将解决您的目的。

      【讨论】:

      • 要么在这个答案中添加代码,要么把它作为评论。事实上,这不是一个答案。
      猜你喜欢
      • 2015-07-01
      • 2015-12-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 2016-01-17
      • 2012-09-02
      相关资源
      最近更新 更多