七爪源码:JavaScript,单线程但非阻塞

当人们说 JavaScript 是一种单线程和非阻塞的编程语言时,那些刚接触 JavaScript 的人可能会感到困惑。 你可能会想怎么可能是单线程但非阻塞的?

七爪源码:JavaScript,单线程但非阻塞

单线程

众所周知,JavaScript 是单线程的,因为它只有一个调用堆栈的属性,而其他一些编程语言有多个调用堆栈。 JavaScript 函数通过 LIFO(后进先出)在调用堆栈上执行。 例如,我们有这样一段代码:

const foo = () => {
  const bar = () => {
    console.trace();
  }
  bar();
}foo();

并且调用栈会有foo进入调用栈,然后bar。

七爪源码:JavaScript,单线程但非阻塞

bar() 完成后,将从调用堆栈中弹出,然后是 foo()。 当打印出堆栈跟踪时,您将在下面看到一个匿名函数,这是主线程的全局执行上下文。

七爪源码:JavaScript,单线程但非阻塞

这似乎是合乎逻辑的,因为 JavaScript 是一种单线程语言,并且只有一个流程来执行所有这些功能。 但是,如果我们在流程中有一些不可预测或繁重的任务(例如进行 API 调用),我们不希望它们阻止剩余代码的执行(否则用户可能会盯着冻结的屏幕) . 这就是异步 JavaScript 的用武之地。

非阻塞

除了 JavaScript 引擎,我们还有 Web API、回调队列和事件循环来在浏览器中形成 JavaScript 运行时。 假设我们这里有一段代码:

console.log("1")
setTimeout(() => console.log("2"), 5000)
console.log("3")

“setTimeout”是一个 Web API 函数,它将在一定时间后执行回调函数(以毫秒为单位,在本例中为 5000 毫秒)。 执行此脚本时,您会看到“1”和“3”立即打印出来,大约 5 秒后打印出“2”。

七爪源码:JavaScript,单线程但非阻塞

这是幕后发生的事情:

第一个控制台日志被放入堆栈并在控制台中打印出“1”后弹出。 当 setTimeout 函数入栈时,回调函数被这个 Web API 函数设置为等待。 然后 setTimeout 函数从堆栈中弹出并进入第三个控制台日志。 执行完成后,第三个控制台日志和当前全局执行上下文从堆栈中弹出。

当 setTimeout 中的回调函数等待完成后,将进入回调队列(或事件队列)等待执行。 事件循环促进并检查调用堆栈是否为空。 如果它为空,则创建一个新的全局执行上下文,然后此回调函数(控制台注销“2”)将被放入堆栈、执行并弹出。

七爪源码:JavaScript,单线程但非阻塞

补充一下,即使你将 setTimeout 设置为延迟 0 秒,“2”仍然是最后一个打印出来的,因为只要调用 Web API,它就会被放入 Callback 队列并被放置 仅当堆栈为空时才进入堆栈。

我希望这能让您了解为什么 JavaScript 可以同时是单线程和非阻塞的。


如果您想查看更多与 Web 开发或软件工程相关的内容,请关注我。

关注七爪网,获取更多APP/小程序/网站源码资源!

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章