异步函数的回调被放入消息队列中并通过事件循环执行。
No.
有一般来说两种异步函数 - 一种在完成时需要某种回调来运行,另一种则生成 Promise 作为结果。
基于回调的函数
这是基于回调的函数的示例:
setTimeout(() => console.log("foo"), 0);
console.log("bar");
/* output:
bar
foo
*/
setTimeout
将延迟稍后执行的回调。即使超时为零毫秒,它仍然会安排回调,直到after当前执行已完成,因此顺序为:
-
setTimeout
安排回调。
-
console.log("bar")
runs.
- 回调与
console.log("foo")
runs.
没有一个message队列,有一个队列tasks执行。作为一个简短的概述,事件循环从队列中获取一个任务并执行它直至完成,然后获取下一个任务并执行它直至完成,等等。一个简单的伪代码表示是:
while(true) {
if (eventQueue.hasNext()) {
task = eventQueue.takeNext();
task();
}
}
有关事件循环的更多信息,请参阅此处。
话虽如此,事件循环的影响更大setTimeout
比许多其他基于回调的函数。基于回调的函数的其他示例是 jQuery 的$.ajax()
或者 Node.jsfs
用于文件系统访问的模块(其非承诺 API)。他们和其他人也接受稍后执行的回调,但边界因素不是事件循环,而是函数执行的任何底层操作。如果是$.ajax()
when回调的调用取决于网络速度、延迟和响应请求的服务器。所以,虽然回调will由事件循环执行,其他一切也是如此。所以,这没什么特别的。仅有的setTimeout
这里有一个更特殊的交互,因为根据可用的任务,计时可能不精确 - 如果您安排某些任务在 10 毫秒内运行,而某些任务需要 12 毫秒才能完成,则回调计划为setTimeout
不会准时运行。
基于 Promise 的函数
这是一个例子:
async function fn(print) {
await "magic for now";
console.log(print);
}
fn("foo")
.then(() => console.log("bar"));
/* output:
foo
bar
*/
(为了便于说明,我现在省略了很多细节。)
从技术上讲,Promise 是针对处理异步操作而定制的回调的抽象。这.then()
Promise 的方法接受回调并执行它after承诺得到解决,这也会在异步操作完成后发生。因此,我们可以按照正确的顺序一起执行:
async function fn(print) {
await "magic for now";
console.log(print);
}
fn("foo");
console.log("bar");
/* output:
bar
foo
*/
在某种程度上,Promise 是一种回调,因为它们是用来替代 Promise 并以大致相同的方式执行的。您仍然可以传递回调以在成功或失败时执行。但他们不是just回调.
无论如何,给予 Promise 的回调是still延迟:
Promise.resolve()
.then(() => console.log("foo"));
console.log("bar");
/* output:
bar
foo
*/
但不通过与事件队列相同的事件队列setTimeout
用途。有两个队列:
- 宏任务队列 -
setTimeout
把东西放在里面,所有的UI交互也都添加到这里。
- 微任务队列 - 承诺在这里安排他们的事情。
当事件循环运行时,微任务队列(即promise)具有最高优先级,然后是宏任务队列。这导致:
setTimeout(() => console.log("foo")); //3 - macrotask queue
Promise.resolve()
.then(() => console.log("bar")); //2 - microtask queue
console.log("baz"); //1 - current execution
/* output:
baz
bar
foo
*/
无论如何,我认为我不太愿意说基于 Promise 的函数通过回调来工作。有点是的,但是是在还原论的意义上。
异步执行是非阻塞的,通过事件循环完成。
No.
什么是“阻塞”?
首先,让我们明确一点——阻塞行为是指环境正忙于执行某些操作时的行为。通常在该执行期间不能运行其他代码。因此,进一步的代码是blocked从跑步。
我们以这段代码为例:
setTimeout(taskForLater, 5000);
while (somethingIsNotFinished()) {
tryToFinish();
}
Here taskForLater
将被安排在 5 秒内运行。但是,那while
循环将block执行。由于没有其他代码将运行,taskForLater
可能会在 10 秒内运行,如果这是循环完成所需的时间。
运行异步函数并不总是意味着它会运行在平行下使用当前代码。在大多数情况下,环境一次执行一件事。 JavaScript 默认情况下没有多线程,并行执行是一种选择行为,例如通过使用workers.
什么是“异步执行”?
这可能意味着几件事,但不清楚您引用的是哪一个:
- 运行异步函数的主体
- 等待底层异步操作完成
在这两种情况下,引用的陈述都是错误的,但原因不同。
异步函数的主体
异步函数是承诺的语法糖。它们使用相同的机制,但只是以不同的方式呈现代码。然而,异步函数正在阻塞。附带说明一下,Promise 执行器(给予 Promise 构造函数的回调)也是如此。在这两种情况下,该函数都会运行并阻止直到有什么事情导致它暂停。演示它的最简单方法是使用异步函数 - 使用await
will暂停该函数的执行并安排其余部分稍后完成。
全身都堵住了:
async function fn() {
console.log("foo");
console.log("bar");
}
fn();
console.log("baz");
/* output:
foo
bar
baz
*/
中间停顿一下:
async function fn() {
console.log("foo");
await "you can await any value";
console.log("bar");
}
fn();
console.log("baz");
/* output:
foo
baz
bar
*/
作为澄清一点,any价值是可以等待的,而不仅仅是承诺。等待非承诺仍然会暂停并恢复执行,但由于没有什么可等待的,这将导致它成为微任务队列上的首要任务之一。
无论如何,执行异步函数的主体might堵塞。要看具体是什么操作。
底层异步操作
当谈论“底层操作”时,大多数时候我们的意思是将控制权交给其他东西,例如浏览器或库。在这种情况下,当前的 JavaScript 环境不会完成该操作,我们称之为将执行操作并仅在操作完成时通知当前 JavaScript 环境的东西。例如,在浏览器中调用fetch
将触发网络请求,但处理它的是浏览器,而不是我们的代码。所以它是非阻塞的,但不是在执行环境之外。
fetch("https://official-joke-api.appspot.com/random_joke")
.then(res => res.json())
.then(joke => console.log(joke.setup + "\n" + joke.punchline));
console.log("foo");
话虽如此,我们甚至无法概括给定的异步操作将做什么。它实际上可能会阻塞执行,至少会阻塞一段时间,或者可能完全在当前 JavaScript 环境的单独进程中执行。
功能类似于setTimeout
是异步的。
Yes.
虽然,它可能正在考虑什么是like setTimeout
. Is it setInterval
?它是基于回调的异步函数吗?问题在于定义开始变成循环“基于异步回调的函数,例如setTimeout
是异步的”。
并非每个接受回调的函数都是异步的。那些are可能被认为类似于setTimeout
.
异步函数是阻塞的,只有它们的回调是非阻塞的。
No.
如上所述,异步函数might堵塞。取决于他们具体做什么。例如$.ajax
会使用浏览器发起网络请求,类似于fetch
。如果是$.ajax
该函数在准备请求时会阻塞,但在发送请求后不会阻塞。
该语句的第二个问题是调用非阻塞回调是不正确的 -执行他们肯定会再次阻止执行。回调是一个普通的 JavaScript 代码,当时间到来时将通过事件循环执行。当任务运行完成时,执行仍然被阻止。
如果您的意思是它们是非阻塞的,因为回调将被放入事件队列中,但这仍然不能保证。考虑以下说明性代码:
function myAsyncFunction(callback) {
const asyncResult = someAsynchronousNonBlockingOperation();
doStuff(result);
callback(result);
doMoreStuff(result);
}
Once someAsynchronousNonBlockingOperation()
产生一个值执行callback
不会安排在稍后,但将成为处理该结果的同步代码序列的一部分。所以,callback
将被处决later但它本身并不是一项任务,而是一项总体任务的一部分,其中包括doStuff
and doMoreStuff
.