关于进程、线程以及执行栈

189 阅读2分钟

1、进程与线程

相信大家都知道JS是单线程执行的,讲到线程就肯定得说下线程。本质上说,两个名词都是CPU 工作时间片的一个描述。

进程描述了 CPU 在运行指令及加载和保存上下文所需的时间,放在应用上来说就代表了一个程序。线程是进程中的更小单位,描述了执行一段指令所需的时间。

把这些概念拿到浏览器中来说,当你打开一个 Tab 页时,其中就创建了一个进程,一个进程中可以有多个线程,比如渲染线程,JS 引擎线程,HTTP 请求线程等等。当你发起一个请求时,其中就是创建了一个线程,当请求结束后,该线程就会被销毁。

上文说到了 JS 引擎线程和渲染线程,当 JS 运行的时候可能会组织 UI 渲染,这说明了两个线程是互斥的,这其中的原因是因为 JS 可以修改 DOM,如果在 JS 执行的时候 UI 线程还在工作,就可能导致不能安全的渲染 UI。这其实也是一个单线程的好处,得益于 JS 是单线程运行的可以达到节省内存,节约上下文切换时间,没有锁的问题的好处。当然前面两点在服务端更容易体现,对于锁的问题,形象的来说当我读取一个数字 15 的时候,同时有两个操作对数字进行了加减,这时候结果就出现了错误。解决这个问题也不难,只需要在读取的时候加锁,直到读取完成执行都不能进行写入操作。

2、执行栈

可以把执行栈认为是一个储存函数调用的栈结构,遵循先进后出的原则。

当开始执行 JS 代码时,首先会执行一个 main 函数,然后执行我们的代码。根据先进后出的原则,后执行的代码会先弹出栈。

在平时开发的过程中,也可以在报错中找到执行栈的痕迹

function foo() {
  throw new Error('error');
};

function bar() {
  foo();
};

bar();

从上图可以看出,报错在 foo 函数,foo 函数又是在 bar 函数中调用的。

当使用递归的时候,因为栈可以存放的函数是有限制的,一旦存放过多的函数且没有得到释放的话,就会出现爆栈的问题

function bar() {
  bar();
};

bar();

---END---