meface/docs/article/web/js/async.md

103 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: JavaScript异步操作
date: 2020-10-20
author: ac
tags:
- JavaScript
categories:
- Web
---
## JavaScript 异步操作
### 单线程模式
JavaScript只在一个线程上运行。即JavaScript同时只能执行一个任务。但不代表JavaScript引擎只有一个引擎。实际上JavaScript引擎有多个线程单个脚本只能在一个线程上运行称为主线程其他线程都是在后台配合。
JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的“事件循环”机制Event Loop
为了利用多核 CPU 的计算能力HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以这个新标准并没有改变 JavaScript 单线程的本质。
### 同步任务和异步任务
- 同步任务:是那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务。
- 异步任务:是那些被引擎放在一边,不进入主线程、而进入【任务队列】的任务。只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。
### 任务队列和事件循环
JavaScript 运行时除了一个正在运行的主线程引擎还提供一个任务队列task queue里面是各种需要当前程序处理的异步任务。
> 实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。
异步任务的写法通常是回调函数。一旦异步任务重新进入主线程,就会执行对应的回调函数。如果一个异步任务没有回调函数,就不会进入任务队列。
**事件循环**
JavaScript 引擎怎么知道异步任务有没有结果能不能进入主线程呢答案就是引擎在不停地检查一遍又一遍只要同步任务执行完了引擎就会去检查那些挂起来的异步任务是不是可以进入主线程了。这种循环检查的机制就叫做事件循环Event Loop
#### 定时器
JS中有两个定时器setTimeout(func|code, delay)和setInterval(func|code, delay)
setTimeout函数用来指定某个函数或某段代码在多少毫秒之后执行。如果推迟执行的是函数就直接将函数名作为setTimeout的参数。它返回一个整数表示定时器的编号以后可以用来取消这个定时器。
```javascript
var timerId = setTimeout(func|code, delay);
clearTimeout(timerId)
```
> 注意当回调函数中存在this时this指向window
setInterval函数的用法与setTimeout完全一致。区别在于setInterval指定某个任务每隔一段时间就执行一次也就是无限次的定时执行。
**运行机制**
setTimeout和setInterval的运行机制是将指定的代码移出本轮事件循环等到下一轮事件循环再检查是否到了指定时间。如果到了就执行对应的代码如果不到就继续等待。
**存在问题**
setTimeout和setInterval指定的回调函数必须等到本轮事件循环的所有同步任务都执行完才会开始执行。由于前面的任务到底需要多少时间执行完是不确定的所以没有办法保证setTimeout和setInterval指定的任务一定会按照预定时间执行。
### 异步操作的模式
1. 回调函数
2. 事件监听
3. 发布/订阅
#### 回调函数
- 优点:简单、容易理解和实现;
- 缺点不利于代码的阅读和维护各个部分之间高度耦合coupling使得程序结构混乱、流程难以追踪尤其是多个回调函数嵌套的情况而且每个任务只能指定一个回调函数。
#### 事件监听
异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。所以通过绑定事件来决定函数调用顺序
- 优点优点是比较容易理解可以绑定多个事件每个事件可以指定多个回调函数而且可以“去耦合”decoupling有利于实现模块化。
- 缺点:整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。
#### 发布/订阅
事件完全可以理解成“信号”如果存在一个“信号中心”某个任务执行完成就向信号中心“发布”publish一个信号其他任务可以向信号中心“订阅”subscribe这个信号从而知道什么时候自己可以开始执行。这就叫做”发布/订阅模式”publish-subscribe pattern又称“观察者模式”observer pattern
### 异步操作的流程控制
当存在多个异步操作时,如何确定异步操作执行的顺序,以及如何保证准守这种顺序
- 串行执行:一个个任务依次执行。当一个任务完成之后,再去执行另一个。
- 并行执行即所有异步任务同时执行等到全部完成以后才执行final函数。效率比1要高但问题是如果并行的任务较多很容易耗尽系统资源拖慢运行速度。
- 串行与并行结合就是设置一个门槛每次最多只能并行执行n个异步任务这样就避免了过分占用系统资源。
### 参考文章
[1] 步操作概述 https://wangdoc.com/javascript/async/general.html