在Node.js中循环使用setTimeout

时间:2018-04-12 07:10:23

标签: javascript node.js async.js

我想在while循环中的setTimeout中执行nodejs执行。我使用了async-waterfall函数,但它在循环中没有工作。所以我使用下面的代码: -

    var i = 3;

    while(i> 0)
    {
        setTimeout(function(){
            console.log('start', i);

            setTimeout(function(){ console.log('timeout');}, 1000);

            console.log('end', i);

            i--;
        }, 1000);

    }
console.log("execution ends");

但我没有得到预期的输出。 我的预期输出将如下: -

    start3
    timeout
    end3
    start2
    timeout
    end2
    start1
    timeout
    end1
    execution ends

6 个答案:

答案 0 :(得分:0)

nodejs本质上是异步的,setTimeout或多或少像在新线程上执行某些东西(或多或少因为JS是单线程并使用事件循环)。

查看这个npm包: https://www.npmjs.com/package/sleep

这应该可以解决问题......
但显然你应该只使用sleep进行调试。 在生产代码中,您最好使用NodeJS的异步性质并使用Promises

查看此内容以获取更多详情: 事件循环:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

setTimeout:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout

承诺:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

答案 1 :(得分:0)

方式1:使用while loop



var i = 3
var p = Promise.resolve(i)
while (i > 0) {
  (i => {
    p = p.then(() => {
      return new Promise(function (resolve, reject) {
        console.log('start', i)
        setTimeout(function () {
          console.log('timeout')
          console.log('end', i)
          resolve()
        }, 1000)
      })
    })
  })(i)
  i--
}
p = p.then(data => console.log('execution ends'))




way2:



function test(i) {
  console.log('start', i)
  setTimeout(() => {
    console.log('timeout')
    console.log('end', i)
    i--
    if (i < 0) {
      return
    }
    test(i)
  }, 1000)
}
test(3);
&#13;
&#13;
&#13;

way3:使用async

&#13;
&#13;
async function test (i) {
  console.log('start', i)
  await new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('timeout')
      console.log('end', i)
      i--
      if (i < 0) {
        reject(i)
      }
      resolve(i)
    }, 1000)
  })
    .then(i => test(i))
    .catch(i => console.log('done', i))
}
test(3)
&#13;
&#13;
&#13;

答案 2 :(得分:0)

您的程序w.r.t与您的预期输出存在一些问题。第一个i--仅在1000ms后才能工作。到那时,太多的while循环会运行。 setTimeOut也是非阻止的,因此即使在第一个execution ends被安慰之前,start也会被安慰。要达到预期的结果,您可以对代码进行一些更改:

var i = 3;var j =3;

    while(j> 0)
    {
        setTimeout(function(){
            console.log('start', i);
            console.log('timeout');
            console.log('end', i);

            i--;
        }, 1000);
        j--;
    }
    if(j == 0){
        setTimeout(function(){
            console.log('execution ends');
        }, 1000);


    }

答案 3 :(得分:0)

请尝试下面的代码段。您可以在timer方法中引入API调用或异步调用来代替setTimeout。

const timer = () => {
    return new Promise(res => {
        setTimeout(() => {
            console.log('timeout');
            res();
        }, 1000);
    });
}

let i = 3;
let temp;

while (i > 0) {
    if (temp !== i) {
        temp = i;
        console.log('start', temp);
        timer().then(() => {
            console.log('end', temp);
            i -= 1;
        });
    }
}

答案 4 :(得分:0)

首先,您必须了解Javascript中的setTimeout()是非阻止的。这意味着它所做的只是安排稍后运行的东西,然后其余代码立即继续运行。

在你的特定代码示例中,你将有一个无限循环,因为while循环一直持续,继续安排越来越多的计时器,但是直到while循环停止,这些计时器都不会运行。并且,在其中一个计时器运行之前,您的循环变量i永远不会被更改,因此while循环永远不会停止。

要理解为什么它以这种方式工作,你必须要了解Javascript和node.js的事件驱动设计。当你调用setTimeout()时,它会调度JS引擎内部的内部计时器。当该计时器触发时,它会在Javascript事件队列中插入一个事件。下一次JS解释器完成它正在做的事情时,它将检查事件队列并将下一个事件拉出队列并运行它。但是,您的while循环永远不会停止,因此它无法进入任何新事件,因此它永远不会运行任何计时器事件。

我将向您展示三种不同的生成所需输出的方法,它们都在可运行的代码片段中,因此您可以在答案中直接运行它们以查看其结果。

第一种技术是通过简单的Javascript完成的,只需在未来的不同时间设置定时器,使各种所需的输出以正确的顺序触发:

改变计时器

&#13;
&#13;
let cntr = 3;
for (let i = 1; i <= 3; i++) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
}
&#13;
&#13;
&#13;

或者,如果你真的希望它是while循环:

&#13;
&#13;
let cntr = 3, i = 1;
while (i <= 3) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
    i++;
}
&#13;
&#13;
&#13;

使用Promise来排序操作

&#13;
&#13;
function delay(t, v) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, v), t);
    });
}

function run(i) {
    return delay(1000).then(() => {
        console.log("start", i);
        return delay(1000).then(() => {
            console.log("timeout");
            console.log("end", i);
            return i - 1;
        });
    });
}

run(3).then(run).then(run).then(() => {
    console.log("execution ends");
});
&#13;
&#13;
&#13;

如果您愿意,也可以将其置于循环中:

&#13;
&#13;
function delay(t, v) {
   return new Promise(resolve => {
       setTimeout(resolve.bind(null, v), t);
   });
}

function run(i) {
    return delay(1000).then(() => {
      console.log("start", i);
      return delay(1000).then(() => {
          console.log("timeout");
          console.log("end", i);
          return i - 1;
      });
    });
}

[3, 2, 1].reduce((p, v) => {
     return p.then(() => {
         return run(v);
     });
}, Promise.resolve()).then(() => {
    console.log("execution ends");
});
&#13;
&#13;
&#13;

Promises Plus ES7 Async / Await

此方法使用ES7的await功能,允许您使用promises和await编写类似顺序的代码,这可以使这些类型的循环更加简单。 await在等待承诺解析时阻止函数的内部执行。它不会阻止函数的外部返回。该功能仍然立即返回。它返回一个promise,当内部函数的所有被阻塞的部分完成时,它将被解析。这就是为什么我们在.then()的结果上使用runSequence()知道它何时完成的原因。

&#13;
&#13;
function delay(t, v) {
   return new Promise(resolve => {
       setTimeout(resolve.bind(null, v), t);
   });
}

async function runSequence(num) {
    for (let i = num; i > 0; i--) {
        await delay(1000);
        console.log("start", i);
        await delay(1000);
        console.log("timeout");
        console.log("end", i);
    }
}

runSequence(3).then(() => {
    console.log("execution ends");
});
&#13;
&#13;
&#13;

await示例说明了promises和await如何简化ES7中的操作顺序。但是,他们仍然要求您了解异步操作如何在Javascript中工作,所以请不要试图首先跳过这种理解水平。

答案 5 :(得分:-1)

你没有得到预期的输出,因为代码中有关闭,改进你的代码:

var i = 3;
            while(i> 0)
            {
                setTimeout((
                    function(i){
                        return function(){
                            console.log('start', i);
                            setTimeout(function(){ console.log('timeout');}, 1000);
                            console.log('end', i);
                            i--;
                        }
                    }
                )(i), 1000);
            }