在 Node.js 的 while 循环中使用 setTimeout
Use setTimeout inside while loop in Node.js
我想在 while 循环内的 setTimeout 中保持 nodejs 执行。我使用了异步瀑布函数,但它在 while 循环中不起作用。所以我使用了下面的代码:-
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
nodejs 本质上是异步的,setTimeout
或多或少就像在一个新线程上执行某些东西(或多或少是因为 JS 是单线程并使用事件循环)。
查看这个 npm 包:
https://www.npmjs.com/package/sleep
这应该可以解决问题...
但很明显,您应该仅将 sleep
用于调试目的。
在生产代码中,你最好接受 NodeJS 的异步特性并使用 Promises
查看此内容了解更多详情:
事件循环:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
设置超时:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
承诺:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
您没有得到预期的输出,因为您的代码中存在闭包,请像这样改进您的代码:
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);
}
方式1:usewhile 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'))
方式 2:
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);
方式三:使用async
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)
您的程序 w.r.t 对于您的预期输出存在一些问题。第一个 i-- 仅在 1000 毫秒后起作用。到那个时候,太多的 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);
}
请尝试以下代码段。您可以在计时器方法中引入 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;
});
}
}
首先,您必须了解 Javascript 中的 setTimeout()
是非阻塞的。这意味着它所做的只是稍后安排一些事情到 运行,然后你的代码的其余部分立即保持在 运行ning。
在您的特定代码示例中,您将有一个无限循环,因为 while 循环一直持续下去,继续安排越来越多的计时器,但直到 while 循环停止,这些计时器中的 none none =97=]。而且,直到其中一个计时器可以 运行,您的循环变量 i
永远不会改变,因此 while
循环永远不会停止。
要了解它为何以这种方式工作,您真的必须了解 Javascript 和 node.js 的事件驱动设计。当您调用 setTimeout()
时,它会在 JS 引擎内部安排一个内部计时器。当该计时器触发时,它会在 Javascript 事件队列中插入一个事件。下次 JS 解释器完成它正在做的事情时,它将检查事件队列并将下一个事件从队列中拉出并 运行 它。但是,您的 while
循环永远不会停止,因此它永远无法处理任何新事件,因此它永远不会 运行 您的任何计时器事件。
我将向您展示三种不同的方法来生成您想要的输出,它们都在 运行 可用的代码片段中,因此您可以 运行 在答案中直接查看它们的结果。
第一种技术是简单地 Javascript 完成的,只需在与未来不同的时间设置计时器,以便以正确的顺序触发各种所需的输出:
可变计时器
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);
}
或者,如果您真的希望它成为一个 while
循环:
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++;
}
使用 Promise 对操作进行排序
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");
});
如果您愿意,也可以将其放入循环中:
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");
});
Promises Plus ES7 Async/Await
此方法使用 ES7 的 await
功能,允许您使用 promises 和 await
编写类似顺序的代码,这可以使此类循环变得更加简单。 await
在等待承诺解决时阻止函数的内部执行。它不会阻止函数的外部return。该函数仍然立即 returns。它 return 是一个承诺,当内部函数的所有阻塞部分都完成时就会解决。这就是为什么我们在 runSequence()
的结果上使用 .then()
来知道何时完成。
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");
});
这个 await
示例说明了 promises 和 await
如何简化 ES7 中的操作顺序。但是,它们仍然要求您了解异步操作在 Javascript 中的工作原理,因此请不要尝试先跳过该理解级别。
我想在 while 循环内的 setTimeout 中保持 nodejs 执行。我使用了异步瀑布函数,但它在 while 循环中不起作用。所以我使用了下面的代码:-
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
nodejs 本质上是异步的,setTimeout
或多或少就像在一个新线程上执行某些东西(或多或少是因为 JS 是单线程并使用事件循环)。
查看这个 npm 包: https://www.npmjs.com/package/sleep
这应该可以解决问题...
但很明显,您应该仅将 sleep
用于调试目的。
在生产代码中,你最好接受 NodeJS 的异步特性并使用 Promises
查看此内容了解更多详情: 事件循环:https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop
设置超时:https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout
承诺:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
您没有得到预期的输出,因为您的代码中存在闭包,请像这样改进您的代码:
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);
}
方式1:usewhile 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'))
方式 2:
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);
方式三:使用async
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)
您的程序 w.r.t 对于您的预期输出存在一些问题。第一个 i-- 仅在 1000 毫秒后起作用。到那个时候,太多的 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);
}
请尝试以下代码段。您可以在计时器方法中引入 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;
});
}
}
首先,您必须了解 Javascript 中的 setTimeout()
是非阻塞的。这意味着它所做的只是稍后安排一些事情到 运行,然后你的代码的其余部分立即保持在 运行ning。
在您的特定代码示例中,您将有一个无限循环,因为 while 循环一直持续下去,继续安排越来越多的计时器,但直到 while 循环停止,这些计时器中的 none none =97=]。而且,直到其中一个计时器可以 运行,您的循环变量 i
永远不会改变,因此 while
循环永远不会停止。
要了解它为何以这种方式工作,您真的必须了解 Javascript 和 node.js 的事件驱动设计。当您调用 setTimeout()
时,它会在 JS 引擎内部安排一个内部计时器。当该计时器触发时,它会在 Javascript 事件队列中插入一个事件。下次 JS 解释器完成它正在做的事情时,它将检查事件队列并将下一个事件从队列中拉出并 运行 它。但是,您的 while
循环永远不会停止,因此它永远无法处理任何新事件,因此它永远不会 运行 您的任何计时器事件。
我将向您展示三种不同的方法来生成您想要的输出,它们都在 运行 可用的代码片段中,因此您可以 运行 在答案中直接查看它们的结果。
第一种技术是简单地 Javascript 完成的,只需在与未来不同的时间设置计时器,以便以正确的顺序触发各种所需的输出:
可变计时器
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);
}
或者,如果您真的希望它成为一个 while
循环:
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++;
}
使用 Promise 对操作进行排序
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");
});
如果您愿意,也可以将其放入循环中:
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");
});
Promises Plus ES7 Async/Await
此方法使用 ES7 的 await
功能,允许您使用 promises 和 await
编写类似顺序的代码,这可以使此类循环变得更加简单。 await
在等待承诺解决时阻止函数的内部执行。它不会阻止函数的外部return。该函数仍然立即 returns。它 return 是一个承诺,当内部函数的所有阻塞部分都完成时就会解决。这就是为什么我们在 runSequence()
的结果上使用 .then()
来知道何时完成。
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");
});
这个 await
示例说明了 promises 和 await
如何简化 ES7 中的操作顺序。但是,它们仍然要求您了解异步操作在 Javascript 中的工作原理,因此请不要尝试先跳过该理解级别。