JavaScript 有类似于 VBA 中的 DoEvents 吗?

12

我在我的代码中有一个长时间运行的循环,我想延迟这个循环以处理事件队列中的其他任务(例如按钮按下)。JavaScript或JQuery是否有任何可以帮助我的东西?基本上,我正在尝试像这样延迟循环 (https://support.microsoft.com/en-us/kb/118468)。

5个回答

7
如果您的应用程序确实需要长时间运行的JavaScript代码,处理它的最佳方法之一是使用JavaScript Web Workers。JavaScript代码通常在前台线程上运行,但通过创建Web Worker,您可以有效地将长时间运行的进程保持在后台线程上,您的UI线程将可以自由响应用户输入。
例如,您可以像这样创建一个新的Worker:
var myWorker = new Worker("worker.js");

您可以在主页面的js中像这样向其发送消息:
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');

请像这样响应worker.js中的消息:

onmessage = function(e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script');
  postMessage(workerResult);
}

有没有办法将点击处理程序移动到Web Worker页面?我知道它们不允许您使用DOM,但我现在无法将代码移动到后台线程(至少现在不行)。 - Ph33ly
1
@GetOtterHere 在你的主页上创建一个点击事件,除了向工作线程发送“按钮被点击”的信息之外,不要执行任何操作。然后当工作线程接收到该消息时,它可以处理该事件。 - Maximillian Laumeister

3

随着ES6中生成器的引入,您可以编写一个帮助方法,使用yield来模拟DoEvents,而不需要太多的语法开销:

doEventsOnYield(function*() {
    ... synchronous stuff ...

    yield;  // Pump the event loop. DoEvents() equivalent.

    ... synchronous stuff ...
});

这是一个帮助方法,它还将函数的完成/失败作为Promise公开:
function doEventsOnYield(generator) {
    return new Promise((resolve, reject) => {
        let g = generator();
        let advance = () => {
            try {
                let r = g.next();
                if (r.done) resolve();
            } catch (ex) {
                reject(ex);
            }
            setTimeout(advance, 0);
        };
        advance();
    });
}

请注意,目前您可能需要通过ES6-to-ES5转译器运行此代码才能在普通浏览器上运行。

1

0

没有与DoEvents完全等价的东西。接近的方法是为每个迭代使用setTimeout

(function next(i) {
    // exit condition
    if (i >= 10) {
        return;
    }

    // body of the for loop goes here

    // queue up the next iteration
    setTimeout(function () {
        // increment
        next(i + 1);
    }, 0);
})(0); // initial value of i

然而,这很少是一个好的解决方案,在Web应用程序中几乎从来不需要。可能有一个你错过了的事件可以使用。你真正的问题是什么?


我正在向打印机发起一系列 AJAX 调用,并尝试实现“取消”按钮。问题在于,如果在任何时候点击该按钮,则代码直到循环完成后才会执行。我希望有一个 DoEvents 功能,可以在每次迭代后检查是否单击了按钮。每当我尝试使用匿名函数的解决方案时,我的 UI 代码就不再运行了。(调试显示值已经存在,但类似 &(selector).dialog('open') 的东西不会显示) - Ph33ly
1
@GetOtterHere:永远不要使用同步XHR。如果将其设置为异步,则此问题将不存在。=) - Ry-
@GetOtterHere 根据您在评论中的澄清,似乎您想要做的是将您的XHR请求更改为异步,而不是使用Web Workers。 - Maximillian Laumeister
@minitech, @MaximillianLaumeister,我刚试着实现了一个Web Worker,但是这一行代码myWorker.postMessage([first.value,second.value]);直到循环结束后才被执行。但我肯定会重新设计XHR请求的。 - Ph33ly

0

这是一个经过测试的示例,演示如何使用Yield作为DoEvents的直接替代。

(我已经使用了Web Worker,它非常好用,但它与DoEvents相距甚远,几乎无法访问全局变量)。这个示例已经格式化以便于理解,并尝试展示如何将所需的额外功能(使函数处理yield)视为原始函数中的插入。 "yield"还有各种其他功能,但在这种情况下,它几乎可以直接替代DoEvents。

//'Replace DoEvents with Yield ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield )
var misc = 0; //'sample external var

function myfunction() { //'This is the beginning of your original function which is effectively replaced by a handler inserted as follows..
//'-----------------------------------Insert Handler..
  var obj = myfuncGen.next(); //'start it
  if (obj.done == false) {
    setTimeout(myfunction, 150); //'adjust for the amount of time you wish to yield (depends how much screen drawing is required or etc)
  }
}
var myfuncGen = myfuncSurrogate(); //'creates a "Generator" out of next. 
function* myfuncSurrogate() { //'This the original function repackaged! Note asterisk. 
//'-------------------------------------End Insert    
  var ms;       //...your original function continues here....
  for (var i = 1; i <= 9; i++) { //'sample 9x loop
    ms = new Date().getTime();
    while (new Date().getTime() < ms + 500); //'PAUSE (get time & wait 500ms) as an example of being busy
    misc++; //'example manipulating an external var
    outputdiv.innerHTML = "Output Div<br>demonstrating progress.. " + misc;
    yield; //'replacement for your doevents, all internal stack state and variables effectively hibernate.
  }
  console.log("done");
}

myfunction(); //'and start by calling here. Note that you can use "return" to return a value except by call backs.
<div id='outputdiv' align='center'></div>

如果你对这些都是新手,要知道如果没有插入和yield关键字,你只会在什么也没发生的情况下等待5秒钟,然后进度{div}将显示“9”(因为对{div}的所有其他更改都是不可见的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接