【问题标题】:jQuery asynchronous function call, no AJAX requestjQuery异步函数调用,无AJAX请求
【发布时间】:2011-10-13 18:12:13
【问题描述】:

这似乎很愚蠢,但我找不到如何使用不涉及某些服务器端请求的 jQuery 进行异步函数调用。我有一个缓慢的函数,它遍历了很多 DOM 元素,我希望浏览器在这个函数运行时不会冻结。我想在调用慢速函数之前显示一个小指示器,然后当慢速函数返回时,我想隐藏指示器。我有以下内容:

$('form#filter', parentNode).submit(function() {
  var form = $(this);
  indicator.show();
  var textField = $('input#query', form);
  var query = jQuery.trim(textField.val());
  var re = new RegExp(query, "i");
  slowFunctionCall(); // want this to happen asynchronously; all client-side
  indicator.hide();
  return false;
});

目前我提交表单,没有显示指标,浏览器死机,然后slowFunctionCall就完成了。

编辑:我使用Vivin's answer,特别是Sitepoint link 来获得以下解决方案:

var indicator = $('#tagFilter_loading', parentNode);
indicator.hide();
var spans = $('div#filterResults span', parentNode);
var textField = $('input#query', parentNode);
var timer = undefined, processor = undefined;
var i=0, limit=spans.length, busy=false;
var filterTags = function() {
  i = 0;
  if (processor) {
    clearInterval(processor);
  }
  indicator.show();
  processor = setInterval(function() {
    if (!busy) {
      busy = true;
      var query = jQuery.trim(textField.val()).toLowerCase();
      var span = $(spans[i]);
      if ('' == query) {
        span.show();
      } else {
        var tagName = span.attr('rel').toLowerCase();
        if (tagName.indexOf(query) == -1) {
          span.hide();
        }
      }
      if (++i >= limit) {
        clearInterval(processor);
        indicator.hide();
      }
      busy = false;
    }
  }, 1);
};
textField.keyup(function() {
  if (timer) {
    clearTimeout(timer);
  }
  /* Only start filtering after the user has finished typing */
  timer = setTimeout(filterTags, 2000);
});
textField.blur(filterTags);

这会显示和隐藏指示器,也不会冻结浏览器。您可以看到隐藏的 DOM 元素,这正是我想要的。

【问题讨论】:

  • 我认为所有的 JS 都在一个线程中运行。所以不可能异步运行函数。但我可能错了:)
  • 根据您访问使您的功能变慢的 DOM 元素的方式,您可能可以加快这部分的速度。您是否使用类或属性过滤器使用 JQuery 访问元素(慢)?该函数是否多次运行,是否有任何方法可以缓存您正在与之交互的元素/ID?您能否通过更改所有受影响元素使用的 CSS 类来执行相同的 DOM 更新?

标签: javascript jquery forms function asynchronous


【解决方案1】:

Javascript 在单线程中运行,因此如果您的函数速度较慢,它阻塞其他所有内容。

更新

这将做一些你想要的,但请记住,它们在 IE 中没有广泛支持(我认为它们将在 IE10 中)。

一些关于 Web Workers 的资源:

这里有一些关于在没有网络工作者的情况下完成多线程的资源。需要注意的是,这不是“真正的”多线程:

【讨论】:

  • 好吧,难怪我找不到关于这个的教程......谢谢。
  • 你先生,过去是正确的,现在一些浏览器支持网络工作者。
  • @nwellcome 我也提到过它,但由于它没有得到广泛支持,因此它不是一个可行的选择。我希望最终它们会得到广泛的支持,因为这将非常有用。
  • 是的,尽管您可以检测到对工作人员的支持并为那些将使用现代浏览器的人提供更好的体验:diveintohtml5.org/detect.html#workers
  • @nwellcome,是的,我看到它计划用于 IE10。但正如您所说,了解 Microsoft 可能会在小而重要的方面“与众不同”!
【解决方案2】:

我打算建议查看超时,但唉。 John Resig(来自 jQuery)的这篇文章解释了 JavaScript 如何处理其单线程。 http://ejohn.org/blog/how-javascript-timers-work/

This article 还解释说:“在 JavaScript 中异步执行函数时要记住的一点是,页面中的所有其他 JavaScript 执行都会暂停,直到函数调用完成。这就是当前所有浏览器执行 JavaScript 的方式,并且可能导致如果您尝试同时异步调用太多东西,则会出现真正的性能问题。长时间运行的函数实际上会为用户“锁定”浏览器。同步函数调用也是如此。”

话虽如此,您可以自己模拟一个异步函数调用,方法是将您正在执行的任何循环分成一个较小的块并使用 setTimeout()。

例如这个函数:

// Sync
(function() {
  for(var x = 0; x < 100000; ++x) {console.log(x)}
})()

// ASync
var x = 0;
setTimeout(function() {
   console.log(x++);
   if(x < 100000) setTimeout(arguments.callee, 1);
} ,1)

【讨论】:

  • 如果你不想使用arguments.callee(从ES5开始在严格模式下是被禁止的),你可以给回调函数一个名字,并在下次调用时使用它的名字。例如:var x = 0; setTimeout(function rec() { console.log(x++); if(x
【解决方案3】:

你可能想要web workers!

编辑:我很惊讶有多少人跳到“不可能”,所以我会详细说明。 Web Workers 是HTML 5 spec 的一部分。它们允许您生成线程以在后台运行脚本而不会阻塞 UI。它们必须是外部js文件,被调用

var worker = new Worker('my_task.js');

并通过事件进行通信。

【讨论】:

  • 可能是什么。该页面显示“与往常一样,后台线程(包括工作线程)无法操作 DOM”。
  • 很好,工作人员无法访问windowdocumentparent,因此没有DOM 操作。
  • @nwellcome,并不是说它们不可能 :) 我对 Web Workers 的可能性感到非常兴奋。问题是它们没有得到广泛的支持(还)。
  • @Sarah:是的,工作人员不访问 DOM,因为 DOM 不是多线程安全的。所以worker应该运行核心计算并让调用者操作DOM。
猜你喜欢
  • 2016-10-22
  • 2013-06-01
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多