-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JS 基础之异步(一) #11
Comments
前排占座,都是好文章,mark |
写的不错,不过,不知道这些基础的东西应用的场景是啥 |
赞哈,请问我可以转载到博客里吗? |
在文章的六、异步与事件里的 |
《深入浅出Node.js》 4.2章 异步编程的优势与难点(73页)中说:
到底是啥子情况 🤣 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
已知,JavaScript 是单线程的,天生异步,适合 IO 密集型,不适合 CPU 密集型,但是,为什么是异步的喃,异步由何而来的喃,我们将在这里逐渐讨论实现。
一、进程与线程
1. 浏览器是多进程的
它主要包括以下进程:
2. 渲染进程(浏览器内核)
浏览器的渲染进程是多线程的,页面的渲染,JavaScript 的执行,事件的循环,都在这个进程内进行:
setInterval
与setTimeout
所在线程,注意,W3C 在 HTML 标准中规定,规定要求setTimeout
中低于 4ms 的时间间隔算为 4ms 。XMLHttpRequest
连接后通过浏览器新开一个线程请求,将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由 JavaScript 引擎执行。注意,GUI 渲染线程与 JavaScript 引擎线程是互斥的,当 JavaScript 引擎执行时 GUI 线程会被挂起(相当于被冻结了),GUI 更新会被保存在一个队列中等到 JavaScript 引擎空闲时立即被执行。所以如果 JavaScript 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
二、单线程的 JavaScript
所谓单线程,是指在 JavaScript 引擎中负责解释和执行 JavaScript 代码的线程唯一,同一时间上只能执行一件任务。
问题:首先为什么要引入单线程喃?
我们知道:
如果 JavaScript 引擎线程不是单线程的,那么可以同时执行多段 JavaScript,如果这多段 JavaScript 都修改 DOM,那么就会出现 DOM 冲突。
你可能会说,web worker 就支持多线程,但是 web worker 不能访问 window 对象,document 对象等。
原因:避免 DOM 渲染的冲突
当然,我们可以为浏览器引入锁 的机制来解决这些冲突,但其大大提高了复杂性,所以 JavaScript从诞生开始就选择了单线程执行。
引入单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这同时又导致了一个问题:如果前一个任务耗时很长,后一个任务就不得不一直等着。
在实例1中,
sum
并不能立刻打印出来,必须在 for 循环执行完成之后才能执行console.log(sum)
。在实例2中,浏览器先打印
1
,然后弹出弹框,点击确定后才执行console.log(2)
。总结:
为了解决这个问题,JavaScript 语言将任务的执行模式分为两种:同步和异步
三、同步与异步
1. 同步
如果在函数
func
返回的时候,调用者就能够得到预期结果(即拿到了预期的返回值或者看到了预期的效果),那么这个函数就是同步的。2. 异步
如果在函数
func
返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的。总结:
JavaScript 采用异步编程原因有两点,
四、异步过程
在执行这段代码时,
fs.readFile
函数返回时,并不会立刻打印data
,只有data.json
读取完成时才打印。也就是异步函数fs.readFile
执行很快,但后面还有工作线程执行异步任务、通知主线程、主线程回调等操作,这个过程就叫做异步过程。工作线程在异步操作完成后通知主线程,那么这个通知机制又是如何显现喃?答案就是就是消息队列与事件循环。
五、消息队列与事件循环
1. 事件循环(eventloop)
主线程不断的从消息队列中取消息,执行消息,这个过程称为事件循环,这种机制叫事件循环机制,取一次消息并执行的过程叫一次循环。
大致实现过程如下:
例如:
其中,主线程:
异步队列:
事件循环是JavaScript实现异步的具体解决方案,其中同步代码,直接执行;异步函数先放在异步队列中,待同步函数执行完毕后,轮询执行 异步队列 的回调函数。
2. 消息队列
其中,消息就是注册异步任务时添加的回调函数。
主线程在发起 AJAX 请求后,会继续执行其他代码,AJAX 线程负责请求
XXX
,拿到请求后,会封装成 JavaScript 对象,然后构造一条消息:其中
callback
是 AJAX 网络请求成功响应时的回调函数。主线程在执行完当前循环中的所有代码后,就会到消息队列取出这条消息(也就是
message
函数),并执行它。到此为止,就完成了工作线程对主线程的通知
,回调函数也就得到了执行。如果一开始主线程就没有提供回调函数,AJAX 线程在收到 HTTP 响应后,也就没必要通知主线程,从而也没必要往消息队列放消息。异步过程中的回调函数,一定不在当前这一轮事件循环中执行。
六、异步与事件
消息队列中的每条消息实际上都对应着一个事件。
其中一个重要的异步过程就是: DOM事件
从异步的角度看,
addEventLister
函数就是异步过程的发起函数,事件监听器函数就是异步过程的回调函数。事件触发时,表示异步任务完成,会将事件监听器函数封装成一条消息放在消息队列中,等待主线程执行。事件的概念实际上并不是必须的,事件机制实际上就是异步过程的通知机制。
另外,所有的异步过程也都可以用事件来描述。例如:
其中关于事件的详细描述,可以看这篇文章: 事件绑定、事件监听、事件委托,这里不再深入介绍。
七、生产者与消费者
生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据,当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞。
从生产者与消费者的角度看,异步过程是这样的:
那么异步的实现方式有哪些喃?
The text was updated successfully, but these errors were encountered: