在JavaScript中同步使用setTimeout

68
我有以下情况:
setTimeout("alert('this alert is timedout and should be the first');", 5000);
alert("this should be the second one");

我希望在setTimeout之后执行该代码,因为在setTimeout之后的代码并不是我自己的代码,所以我不能把它放到setTimeout函数中调用...

有什么解决方法吗?


这个回答解决了您的问题吗?JavaScript 中类似 sleep() 的函数有哪些? - lmat - Reinstate Monica
11个回答

66

这段代码是否包含在一个函数中?

function test() {
    setTimeout(...);     

    // code that you cannot modify?
}
在这种情况下,您可以阻止函数继续执行,然后再次运行它:
function test(flag) {

    if(!flag) {

        setTimeout(function() {

           alert();
           test(true);

        }, 5000);

        return;

    }

    // code that you cannot modify

}

这太棒了!但我的情况完全相似,除了有很多框架代码坐在setTimeout调用之上,而且它不能再次运行...从setTimeout开始,将我的代码拆分成不同的函数也是不可能的。 - mickeymoon
4
可以采用async/await和Promise方法将异步代码转换为同步代码,但必须将异步代码放入一个函数中才能实现。如果想要在不使用函数的情况下使代码同步化,则需要使用一些比较高级的技巧。 - Parag Gangil
我刚刚创建了一个小型库 https://gitlab.com/ghit/syncjs,用于创建伪同步的JavaScript执行。 - Harun
我想知道后台正在发生什么?setTimeOut的作用是“安排”函数以便稍后执行,将函数作为回调推送到事件循环队列中似乎更合理... - younes zeboudj

45

上周,我遇到了需要类似功能的情况,这让我想起了这篇文章。基本上,我认为AndrkKR提到的“忙等待”在很多情况下都是一个合适的解决方案。下面是我用来占用浏览器并强制等待条件的代码。

function pause(milliseconds) {
 var dt = new Date();
 while ((new Date()) - dt <= milliseconds) { /* Do nothing */ }
}

document.write("first statement");
alert("first statement");

pause(3000);

document.write("<br />3 seconds");
alert("paused for 3 seconds");

请记住,这段代码实际上会阻塞您的浏览器。

希望能对任何人有所帮助。


2
实际上,这并不会暂停脚本的执行,但是在调用暂停函数后,下一个脚本将在循环完成后执行。虽然有些棘手,但从功能上来说,它满足了问题的需求。 - Oki Erie Rinaldi
这与无限 while 循环有何区别?因为如果我运行无限 while 循环,页面会崩溃,但只要条件未满足,这不就是一个检查时间数百万次的循环吗?是什么阻止它崩溃并且在更长时间间隔内高效吗? - Abhishek Choudhary

28

使用ES6、Promise和async,您可以实现同步运行事物。

那么这段代码是在做什么?

// 1. Calls setTimeout 1st inside of demo then put it into the webApi Stack
// 2. Creates a promise from the sleep function using setTimeout, then resolves after the timeout has been completed;
// 3. By then, the first setTimeout will reach its timer and execute from webApi stack. 
// 4. Then following, the remaining alert will show up.


function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function demo() {
  setTimeout("alert('this alert is timedout and should be the first');", 5000);
  await sleep(5000);
  alert('this should be the second one');
}
demo();

1
出色的答案!每个人都应该在可能的情况下这样做。 - Ahm23
希望你不需要IE支持,因为它不支持异步函数。 - Chris
完美。简单易懂。 - DexieTheSheep
5
@Chris,你所说的IE是什么? ‍♂️ - Nathan

8

只需将它放在回调函数内:

setTimeout(function() {
    alert('this alert is timedout and should be the first');
    alert('this should be the second one');
}, 5000);

2
但是他没有访问触发setTimeout的代码的权限? - Marko
由于setTimeout后面的代码并非我自己的代码,因此我无法将其放在setTimeout调用的函数中... 我正在使用一个框架,所以我不能只是把框架的代码放在那里... - Nathan
抱歉误读了。那么你就没那么幸运了。setTimeout 总是异步的。 - Darin Dimitrov
老兄,这是异步的。 - A.R Naseef

6

不,由于Javascript没有延迟函数,除了忙等待(这会锁定浏览器),没有其他方法可以做到这一点。


请您详细阐述一下忙等待的概念及其工作原理。 - Parag Gangil
6
var until = new Date().getTime() + 3000; while(new Date().getTime() < until) {}; alert('3 seconds passed');这段代码的意思是:先获取当前时间加上三秒钟的时间点,然后在这个时间点之前一直循环等待。当时间达到设定时间后,弹出一个提示框显示"3 seconds passed"。 - AndreKR
我总是避免使用while进行检查而没有任何节流。 - tom10271

3
您可以创建一个Promise,并等待其完成
const timeOut = (secs) => new Promise((res) => setTimeout(res, secs * 1000));

await timeOut(1000)

3

ES6 (busy waiting)

const delay = (ms) => {
  const startPoint = new Date().getTime()
  while (new Date().getTime() - startPoint <= ms) {/* wait */}
}

使用方法:

delay(1000)

不会在循环中有所帮助 - Igor Fomenko

2

1
setTimeout(function() {
  yourCode();    // alert('this alert is timedout and should be the first');
  otherCode();   // alert("this should be the second one");
}, 5000);

1

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