通过JS的setTimeout理解线程/同步性

4
Javascript的setTimeout函数让我重新审视了我对Javascript的了解。今天早些时候,我遇到了这样一个for循环:
for (i = 0; i < length; i++) {
    setTimeout(executeOtherCode, 5000)
}

我原本期望这段代码执行executeOtherCode函数,"休眠"5秒钟,并继续进行下一个迭代。然而,实际上我得到的结果是executeOtherCode同时执行了length次。
据我理解,setTimeout是一种异步的函数调用。这个理解正确吗?但是,如果我执行一个普通的函数,比如叫做hugeFunction(),需要执行1分钟才能返回,那么下面的代码行不会执行,对吗?那两者为什么不同呢?只是语言设计的选择吗?
我见过其他在jQuery中表现出类似异步方式的函数,比如getJSON。只要知道哪些函数被定义为异步的就可以了吗,还是有某种模式可以识别它们?如果有的话,是什么?

通常异步函数是处理通过网络或某种I/O操作检索数据的函数(例如,HTML5的新IndexedDB和WebWorkers功能中的一些方法)。除此之外,没有真正一致的方法来识别它们;您只需查看您使用的库的文档即可。 - Matt Browne
1
“而不是我得到的是同时执行其他代码”,不,你没有。该代码将 executeOtherCode() 排队等待在 5000ms 后被调用 length 次,在此期间当前块继续执行。每个超时尽可能接近 5000ms 后启动,但不会在其他 JS 已经执行时启动,因此每个超时都一个接一个地执行,而不是同时执行,也不是多线程。只是因为每个超时之间没有明显的延迟,所以看起来是同时执行的。 - nnnnnn
1个回答

6
通常情况下,除了特殊情况外,JavaScript会同步有序地执行。
setTimeout(executeOtherCode, 5000)

告诉程序“在5秒内,运行函数executeOtherCode”。它会持续运行循环,不会“阻塞”执行。

在5秒后,事件循环会注意到已经设置了一个(或多个)定时器,并依次执行它们。

如果你想让这些函数以5秒的间隔相互执行,你需要告诉下一个函数在上一个函数完成之后5秒再执行,这种模式被称为异步信号量。

通常的经验法则是如果涉及I/O,就应该使用异步方式。这就是为什么AJAX是异步的(以及其他HTTP请求,如脚本标签注入),而交互事件也是异步的(例如,JavaScript 对点击作出反应,而不是等待点击)。定时器(setTimeout 和 setInterval)也是异步的。

其他所有操作都是同步的。

现在,一些函数可能使用这些其他函数,但是没有银弹可以确定哪些函数是阻塞型函数和非阻塞型函数。最好的方法是提供清晰的文档。大多数异步函数都有一个回调参数(比如executeOtherCode的第一个参数),但有些函数没有,而有些函数虽然接受回调函数但本身不是异步的。


+1。大多数JavaScript都是以单线程方式执行的(除了HTML5 WebWorkers)。 - Alexei Levenkov

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