这几天费尽心思想写一个可以实现同步的队列,用于将爬虫的流程同步执行(因为如果同一时间的并发过大的话容易被服务器拒绝),所以特地研究了一下异步转同步的方法。(不过最终因为设计过于复杂而放弃了,有时间再看看async和await)

异步和回调的关系

之前我搞混了这个概念,即我认为回调即异步任务其本身。实际上这样理解是不正确的,异步任务中异步执行的不包含回调。当异步任务产生时异步会被放在任务表,而回调在异步任务执行完毕后才放入任务队列

如下面这个经典的实例中

setTimeout(function () {
  console.log("After 1000ms.");
}, 1000);

被异步执行的任务是setTimeout本身,而回调则是中间的function部分。执行顺序为 setTimeout -> function

在JS中有至少两个与异步相关的队列(这里不谈微任务)。一个叫做事件表(Event Table),另一个叫做任务队列(Task Queue)。前者是用于存放产生的异步任务,即表示需要处理的任务队列;后者则是存放处理完毕后的结果,即带有异步处理结果需要同步执行回调的队列。关于为什么这样设计可以参考下面。

事件的响应与处理

可能很多人都听说过JS是单线程的,但实际上真的是这样吗?并非如此。如果JS是单线程的,那么它不可能实现在执行异步任务时响应新的任务。那么为什么说JS是单线程的呢?其实JS的单线程指的是用户编写的代码一定是以单线程形式执行的,而并不是说它在实现上是单线程的。

既然前面已经谈到了事件表的概念,那么我们在这里就能更容易的去理解事件的处理机制。当事件被触发以后会被放入事件表,协调线程依会次将事件分配给闲置的线程执行,处理完毕后才会将结果和回调放入任务队列。而主线程会循环的检测任务队列是否为空,如果不为空则取出回调执行。这个循环过程我们一般叫做Event Loop,即事件循环

当任务被分配出去后实际上是以多线程执行的,所以说JS无法实现多线程倒是有些太绝对了。不过貌似现在确实有新的机制可以让JS在同步执行时也能有多线程优化,不过我个人认为在JS强大的异步机制下,线程依然是不必要的。

本文小结