【问题标题】:Transforming Sync functions to Async将同步函数转换为异步
【发布时间】:2015-06-25 18:46:11
【问题描述】:

我正在尝试将一系列同步调用变为异步调用,并且我正在使用 when...done...最近两天,我下面的代码应该可以工作。嗯,它确实有效,但并不完全按照我的意思。

在下面的测试代码中,我进行了五次调用,每次调用都会花费不同的时间(在我的示例中,它基于字符串的长度...每个字符 1/2 秒)。我的期望是花费更少时间的调用(即在我的示例中长度较短的字符串)将首先完成,我会快速触发所有五个调用,并期望结果按照执行时间较短到执行时间较长的顺序显示。

这是我的代码:

var cars = ["Saab", "Chrysler", "Volvo", "BMW", "GT"];
loopAll(cars);
document.getElementById("demo").innerHTML += "Start!<br>";
function loopAll(array) {
  for (var i in array) {
   $.when( encapsulate(array[i]) ).done( printIt );
  }
  function encapsulate(name){
    var a = $.Deferred();
    setTimeout(function(){pause(name.length*500);a.resolve(name);}, 0);
    return a;
  }
}
function printIt(name){
  document.getElementById("demo").innerHTML += name + "<br>";
}
function pause(ms) {
  ms += new Date().getTime();
  while (new Date() < ms){}
}

数组中的每个元素都被打印出来,我可以看到它们一个一个地出现在显示器上。但是,它们似乎是同步的,因为它们以数组的原始顺序显示,并且在每个显示的元素之间发生了暂停。然而,我的期望是较短的执行函数比较长的函数更快地完成(和显示)。

注意:我不是要对数组进行排序。我的意图是触发多个调用并让较短的执行调用首先完成,而不是按顺序完成。我仅以字符串数组为例。

【问题讨论】:

  • setTimeout encapsulate 似乎设置为 0 ?
  • @guest271314,是的。在这个网站上阅读,我认为这是立即启动该代码的好方法,有效地为该代码创建一个新线程。
  • "在下面的测试代码中,我进行了 5 次调用,每次调用的时间都不同(在我的示例中,它基于字符串的长度... 1/每个字符 2 秒)。” ?如果在encapsulate 创建的每个$.Deferred 的每个setTimeout duration 设置为0,则在encapsulate 调用的setTimeout 中的每个duration 使用的字符串长度not ? , 而0 出现在每次迭代中?

标签: javascript jquery asynchronous .when


【解决方案1】:

在下面的测试代码中,我进行了五次调用,每一次调用一次 不同的时间量(在我的例子中,它基于 字符串...每个字符 1/2 秒)。

setTimeout(function(){pause(name.length*500);a.resolve(name);}, 0);

对于创建的每个 $.Deferred() 对象,duration 似乎设置为 0?不确定pause 的预期结果?

我的期望是花费更少时间的调用(即 在我的示例中长度较短的字符串)将首先完成 所有五个电话都很快,并希望显示结果 按执行时间从短到长的顺序排列。

尝试将setTimeoutduration 设置为name.length*500


var cars = ["Saab", "Chrysler", "Volvo", "BMW", "GT"];
loopAll(cars);
document.getElementById("demo").innerHTML += "Start!<br>";
function loopAll(array) {
  for (var i in array) {
   $.when( encapsulate(array[i]) ).done( printIt );
  }
  function encapsulate(name){
    var a = $.Deferred();
    setTimeout(function(){a.resolve(name);}, name.length*500);
    return a;
  }
}
function printIt(name){
  document.getElementById("demo").innerHTML += name + "<br>";
}

// function pause(ms) {
//  ms += new Date().getTime();
//  while (new Date() < ms){}
// }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="demo"></div>

【讨论】:

  • 超时块中有一个不必要的分号。
  • 对不起,也许我没有解释我的代码的意图。在我将使用的实际情况下,我不知道该函数调用需要多长时间。我只是通过使用字符串的长度来模拟一个随机持续时间的调用。综上所述,我需要遍历数组中的一些对象,并对它们进行异步处理,但是我正在做的函数调用不是异步的。
  • @tagale 是的。可以指出 “我只是通过使用字符串的长度来模拟随机持续时间调用” 设置在 OP 的位置吗?在 cars 数组的每次迭代中似乎都设置为 0
  • 另外,setTimeout 值为零并不是使代码同步执行的原因。事实上,如果我增加该值,它只会进一步延迟该循环中任何内容的执行。提醒:我使用 setTimeout 作为启动新执行线程的一种方式,而不是为执行添加任何时间。
  • @guest271314,我真正的要求纯粹是为数组中的每个项目启动所有命令,并快速执行循环。我确实做到了,所以我真的不需要再处理这个问题了。循环确实启动每个命令并退出循环,将每个命令放入 js 队列中。但是,令我惊讶的是,这些执行的顺序仍然是顺序的,而不是并行的,我发现这是因为 JS 并不是真正的多线程,而只是在时间可用时排队执行。
【解决方案2】:

在这种情况下,超时块实际上并没有做任何事情。它将立即执行其功能块。

JavaScript 是单线程的,通过使用自制的“暂停”方法,您可以有效地阻塞线程并导致每个循环同步执行。

如果您有一些同步代码,并且没有可用的异步替代方案,您可以使用 Web Worker 将它们从主线程中移出。

http://www.html5rocks.com/en/tutorials/workers/basics/

【讨论】:

  • +1; setTimeout 不会像您在其他评论中所说的那样创建“新的执行线程”;它只是安排一个函数在未来某个时间运行。运行时,该函数仍会在单线程上运行,并且仍会阻塞执行。
  • 我绝对不确定如何执行此操作,但我可以保证 setTimeout 不会阻止执行。如果一个人要运行这段代码,他们会看到循环调用之后立即执行的“打印”语句。换句话说,“开始!”消息会立即显示,即使它是在所有循环代码之后。
  • setTimeout 可以将代码执行推迟到下一个滴答声(或以后的某个日期),但我向您保证,仍然块内的代码会同步执行。例如,请参阅我的答案。
  • 所以我想我知道 setTimeout 并没有真正添加另一个线程。即使它没有阻塞执行,一旦主线程完成,setTimeout 事件队列也会按顺序运行。嗯...谢谢!
【解决方案3】:

您列出的代码有点红鲱鱼;事实证明,问题出在“我正在尝试将一系列同步调用变为异步”的语句中。在大多数 JavaScript 环境中,这根本不可能。

JS 环境通常是单线程的,因此即使您使用setTimeout 来安排未来一段时间的代码执行,该代码也会仍然阻塞线程。示例见this JSFiddlehttp://jsfiddle.net/BinaryMuse/r4ct92c5/

除非您在 Node 中使用 Web Workers(在浏览器中)或 external processesfibersnative addons 之类的东西,否则您无法异步执行 JS 代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 2018-01-11
    • 2018-03-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多