【问题标题】:Javascript promise execution timing issueJavascript承诺执行时间问题
【发布时间】:2021-03-16 02:34:27
【问题描述】:

我刚开始使用 JS 承诺概念,我无法理解为什么在这个例子中两个单独的承诺实际上是“依赖”的(也就是说,为什么如果只声明了“var promise”,执行时间是 2.5 秒,但是当“var promise”和“var promise2”都声明时,两者的执行延迟都是10s)。

如果两者都声明了,并且只执行了 Promise,我预计它会在 2.5 秒内运行。然而,一旦两者都被声明,console.log 输出会以相同的、更长的延迟出现。这是为什么?如果我添加 Promise.all,我希望console.log 输出在 2.5 秒出现,Promise.all 在 10 秒出现,但两者同时出现。

function runSlow(delay) {
  const start = Date.now();
  while (Date.now() - start < delay) { } // force a loop to wait 5 seconds is 5000
  return true;
}

$(document).ready(function () {

    var promise = new Promise(function(resolve, reject) {
      // do a thing, possibly async, then…

        var it=runSlow(2500); 
        

      if (it) {
        resolve("Stuff worked!");
      }
      else {
        reject(Error("It broke"));
      }
    });


    var promise2 = new Promise(function(resolve, reject) {
      // do a thing, possibly async, then…

        var it=runSlow(10000);
        
      if (it) {
        resolve("Stuff worked too!");
      }
      else {
        reject(Error("It broke too"));
      }
    });


    promise.then(function(result) {
      console.log(result); // "Stuff worked!"
    }, function(err) {
      console.log(err); // Error: "It broke"
    });



    /*
    Promise.all([promise, promise2]).then(values => { 
      console.log(values);   
    });
    */



});
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"&gt;&lt;/script&gt;

【问题讨论】:

  • 您的runSlow() 函数是一种可怕的、可怕的延迟执行方式;这是对 CPU 资源的同步浪费。
  • 这里有一个小玩意儿:jsfiddle.net/e35uv12o
  • Promise 是一种处理异步进程的策略,而不是运行缓慢的进程。
  • Promise 不会使紧密的同步循环(如你的 while 循环)异步..
  • @edaus 还请注意,当您拒绝承诺时,不需要 Error() 。你可以直接拒绝它。

标签: javascript promise


【解决方案1】:

首先,您需要了解runSlow 函数中的while 循环会阻塞线程。这意味着在您调用 runSlow(2500) 的 Promise 中,代码在继续之前被阻塞(等待)2.5 秒。因此,无论您如何编写代码,它始终会花费您指定的最长的runSlow() 函数。代码基本上没有异步。如果您在代码中尝试以下操作,您会看到这一点:

// your code above

promise.then(val => console.log("I am done, 2.5 secs later", val))

console.log("I should show up immediately")

您会注意到console.log("I should show up immediately") 并没有立即出现,而是延迟了 2.5 秒。

异步延迟函数的“正确”方法是使用以下代码:

function runSlowAsync(delay) {
  return new Promise(resolve => {
    setTimeout(resolve, delay, true)
  })
}

runSlowAsync 函数上方返回一个 Promise,它在解析后将具有值 true。下面是带有异步 runSlow 函数的代码:

var promise = new Promise(async function(resolve, reject) {
  // do a thing, possibly async, then…

  var it = await runSlowAsync(2500); 

  if (it) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke")) 
  }
});

var promise2 = new Promise(async function(resolve, reject) {
  // do a thing, possibly async, then…

  var it = await runSlowAsync(10000);
        
  if (it) {
    resolve("Stuff worked too!");
  }
  else {
    reject(Error("It broke too"));
  }
});

现在,promise 是如何解决的?

如果我们继续上面的代码,当你使用promise.then(() =&gt; { //Do something }) 时,你是在告诉运行时,好的,一旦这个承诺得到解决,then 做点什么。然后每个承诺将独立执行。如果你使用Promise.all([promise, promise1]).then(),你是在告诉运行时等到所有的promise都被解决,然后then做某事。因此,在这种情况下,只有当运行时间最长的一个已解决时,您的承诺才会得到解决。代码如下:

promise.then(() => console.log("I am done 2.5 secs later"))
promise1.then(() => console.log("I am done 5 secs later"))

Promise.all([promise, promise1]).then(() => console.log("All promises resolved"))

// In your console you will see this.

// after 2.5 seconds
// "I am done 2.5 secs later"
// another 2.5 seconds later (after 5 seconds)
// "I am done 5 secs later"
// "All promises resolved"

【讨论】:

  • 这一行出现错误:var it = async runSlowAsync(2500);即使我在上面声明了函数..
  • @edaus 是的,对不起。那应该是等待,而不是异步。请参阅修改后的答案。
  • 知道了!感谢 Sebastian Ammon 花时间提供扩展解释。在您和 Kaiido cmets 之间,我想我对这一切是如何运作的有一个完整的了解。问我是否可以:将异步函数编写为 Promise 还是仅从 Promise 中引用它们更好?例如 AJAX 调用? AJAX结果的同步处理怎么样?
  • async 和 await 只是处理 Promise 的语法糖。基本上 async 和 await “使”您的代码同步(在 async 函数中)。这取决于你更喜欢什么。一个很好的资源在这里:javascript.info/async-await
【解决方案2】:

查看 cmets,然后运行 ​​sn-p

function runSlow(delay) {
  // you were using while, that will block JS and lead to wrong result
  // this wont go wrong so no reject, but real world logic might
  return new Promise(resolve => setTimeout(() => resolve("Stuff worked!"), delay))
}

// an async method that costs 2.5 sec
runSlow(2500).then(() => console.log(`2.5s later [2500]`))

// no doubt 10 sec
runSlow(10000).then(() => console.log(`10s later [10000]`))

// wait all things done
Promise.all([runSlow(2500), runSlow(10000)]).then(() => console.log(`10s later [all(2500,10000)]`))

// wait single done
Promise.race([runSlow(2500), runSlow(10000)]).then(() => console.log(`2.5s later [race(2500,10000)]`))

【讨论】:

    【解决方案3】:

    Promise 构造函数同步运行。

    所以你做的是真的

    blockThreadFor5s();
    scheduleMicroTask();
    
    blockThreadFor5s();
    scheduleMicroTask();
    

    Promise 反应将在下一个微任务检查点上执行,这只会在所有同步代码都执行后发生,即在 blockThreadFor5s 之后。

    【讨论】:

    • 啊,MicroTasks ......然后回到阅读更多内容。谢谢!
    • 此时这些是微任务并不重要。如果您更熟悉,您甚至可以通过记住 setTimeout 来简化概念,最终结果在这里是相同的。重要的一点是你传入的函数 fn new Promise(fn) 是同步运行的,而 .then(callback) 中的 callback 将异步执行。
    猜你喜欢
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    • 2020-01-22
    • 1970-01-01
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    相关资源
    最近更新 更多