我有一个async,await和setTimeout()的问题。 我想,我使用异步函数来处理慢进程。所以我尝试了一个大循环。在我的计算机上,运行以下代码需要几秒钟时间:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
}, 0);
});
};
console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
console.log('Call slow function.');
console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
输出结果为:
Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:20
Ready at 16:39:23
现在的问题是:下一个代码有什么不同:
function slowFunction() {
return new Promise(resolve => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
});
};
console.log('Start: ' + new Date().toLocaleTimeString('de'));
(async () => {
console.log('Call slow function.');
console.log(await slowFunction());
})();
console.log('There is no need to wait for the slow function: ' + new Date().toLocaleTimeString('de'));
输出结果为:
Start: 16:39:20
Call slow function.
There is no need to wait for the slow function: 16:39:23
Ready at 16:39:23
通过第一个示例,它看起来像异步。通过第二个例子,函数等待循环结束。
我是否必须使用setTimeout或者代码中是否有错误或者我错了?在这两种情况下,解决方案都在大循环之后。
async和await的大多数示例都使用了setTimeout,但我认为,它只是为了模拟休息。
提前感谢您的帮助。
最好的问候 帕斯卡
答案 0 :(得分:5)
Promises和async
函数不会将代码卸载到另一个线程。如果要将长时间运行的进程从主线程移开,请在浏览器上查看web workers,然后在Node.js上查看child processes。
Promises和async
函数(这只是创建和使用promises的语法)不会将您的处理移动到任何其他线程,它仍然发生在您启动该进程的同一个线程上。他们所做的唯一事情是确保异步调用then
和catch
回调。它们不会使您的代码异步(除了那一点,确保回调异步发生)。
因此,使用setTimeout
的第一个块只设置超时,返回一个promise,然后当超时到期时,它会阻止主线程,同时执行慢速运行的进程。这只是在阻塞发生一点点时发生变化,它不会改变阻塞的事实。
你可以在这里看到这个效果,注意当长时间运行的过程发生时计数器暂停的方式:
function slowFunction() {
return new Promise(resolve => {
setTimeout(() => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
}, 1000);
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
你的第二个块没有使用setTimeout
只是立即阻塞,因为promise执行器函数(你传递的函数new Promise
)立即同步运行,而你没有做任何事情让它异步
你可以在这里看到;计数器立即暂停,而不是稍后:
function slowFunction() {
return new Promise(resolve => {
const stop = Date.now() + 2000;
while (Date.now() < stop) {
// busy wait (obviously, never really do this)
}
});
};
console.log("before slowFunction");
slowFunction()
.then(() => {
console.log("then handler on slowFunction's promise");
})
.catch(console.error);
console.log("after slowFunction");
let counter = 0;
const timer = setInterval(() => {
console.log(++counter);
}, 100);
setTimeout(() => {
clearInterval(timer);
console.log("done");
}, 3000);
.as-console-wrapper {
max-height: 100% !important;
}
在长时间运行的代码完成之前,我们甚至没有看到之前的 日志,因为浏览器永远没有机会重新绘制,我们让线程陷入困境。
关于async
函数:async
函数中的代码开始同步,并且在第一个await
(或其他构造,如setTimeout
,计划稍后执行的事情)。只有那之后的代码是异步的(因为它必须等待)。
以下是一个证明:
的例子
async function foo() {
console.log("before await");
await Promise.resolve();
console.log("after await");
}
console.log("before foo");
foo()
.then(() => {
console.log("then handler on foo's promise");
})
.catch(console.error);
console.log("after foo");
这是输出:
before foo before await after foo after await then handler on foo's promise
注意在等待之前在之后 之前的;它与foo
的调用同步。但是等待之后的直到稍后才会发生(因为await Promise.resolve()
必须使其后面的代码异步发生;它是then
的语法糖,它承诺不会调用它即使承诺已经解决,也会同步处理。)
答案 1 :(得分:0)
不同之处在于这是完全同步的代码:
return new Promise(resolve => {
for (let i = 0; i < 4000000000; i++) {};
resolve('Ready at ' + new Date().toLocaleTimeString('de'));
});
此语句将阻止JavaScript线程并强制它等待所有这40亿次迭代发生。 然后它将继续下一个语句。由于console.log
在此之后执行,因此在该循环结束之前不会执行。
这就是你看到差异的原因。