探秘 JS 异步
JavaScript 除了“闭包”这个最经典的设计之外,还有它是“单线程”的设计,一样可奉为最经典!
这里先抛出 3 个经典的问题:
- “JavaScript 为什么要是单线程?”
- “JavaScript 的单线程,意味着什么?”
- “JavaScipt 异步原理是怎么实现的?”
如果你能清晰准确地回答出这3个关于异步老生常谈的经典问题,可以跳过下一小节的释义。
经典 3 问
先浅答一下 JS 异步经典 3 问 ~
“JavaScript 为什么要是单线程?”
答:四字概括,为了:“简单方便”。JavaScript 最初设计只是运行在浏览器的脚本语言,若同一时间要做多件事情便会产生矛盾;不像其它后端语言用“锁”这样一个机制,也为了极致简单,所以 JavaScript 设计是单线程的。
“JavaScript 的单线程,意味着什么?”
答:单线程意味着任务需要排队,任务是一个接一个地执行,前一个执行完毕,才会执行下一个。这就意味着前一个任务的执行会阻塞后续任务的执行。
好比去银行办理业务,目前只有一个人工窗口,前面有个人要办理大额贷款业务,需要填写很多表格,只有等这人把全部表格都填完,整个流程都走完,才能让后面的人接着办业务。
现实中如果发生这样的事,肯定要被投诉,哪有这样设计的?让后面这么多人干等他填表格,并且这个时候窗口服务也是停止的,那效率得多低呀。
所以,正确的做法是,先将这个人挪到一边,让他去填表格,把窗口服务腾出来给后面的人继续办业务,等表格填完了,再回过头来给你办理大额贷款。
将这个比喻映射到 JavaScript 也是同样的逻辑,JavaScript 通过异步来解决单线程阻塞的问题。这也是 与生俱来 就已经设定好了的(和闭包一样,都写在 DNA 里)。
“JavaScipt 异步原理是怎么实现的?”
答:JS 引擎通过混用 2 种内存数据结构:栈和队列 来实现异步。栈与队列的交互也就是大家所熟知的 JS 事件循环(Event Loop)。
简单来讲:所有同步任务都是在主线程上执行的,形成 执行栈,异步任务的回调消息形成 回调队列。在执行栈中的任务处理完成后,主线程就开始读取任务队列中的任务并执行。按这个规则,不断往复循环。
上一张经典的图:
这里的 Stack 就相当于是前面所提银行场景中的唯一人工窗口,Stack 里面的任务就是等待办业务的人,遇到办大额贷款、填很多表格的人,则先挪到一边去,然后继续处理后面人的业务。若这人表格全填完了,就把这个消息放到 CallBack queue 里,等 Stack 里为空后,再去拿 callBack queue 的消息,继续为你解决大额贷款。
以上三问,老生常谈,温故知新。
新 3 问
好了,老 3 问只是开始的小结,这里本瓜要问异步新 3 问:
- “JavaScript 实现异步有哪几种表现形式?”
- “JavaScript 异步和函数式有什么关系?”
- “JavaScript 异步真的简单吗?”
在脑袋里面简单过一过你的答案?
。。。。。。
下面来逐一详细解答~~
异步演进
“JavaScript 实现异步有哪几种表现形式?”
答:
① 回调函数
最简单实现异步就是使用回调函数。
打个比方,以打电话给客服为例,你有两种选择:排队等待客服接听 或 选择客服有空时回电给你。
后面一种就是回调 —— CallBack