异步编程
并发和并行
知乎上的高赞回复
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
所以我认为它们最关键的点就是:是否是『同时』。
并发是宏观概念,指的是具备任务切换的能力。
并行是微观概念,指的是具备处理多任务的能力。因此单CPU不能实现并行。
回调函数
缺点
- 容易造成回调地域,也就是嵌套函数,存在耦合性,不利于维护
- 难以处理错误,也无法用
try{...}catch(err){...}
捕捉错误 - 不能直接
return
Cenerator
- 定义
function*
返回一个Generator对象
- 作用
- 控制函数的执行
- 执行过程
- 调用
next()
方法时,函数执行到出现yield
处暂停执行next()
方法返回一个对象,包含两个属性:value
和done
,value
属性表示本次yield
表达式的返回值,done
表示生成器后续是否还有yield
语句,即生成器函数是否已经执行完毕并返回
- 如果函数中用的是
yield*
,则表示将执行权移交给另一个生成器函数(当前生成器暂停执行) - 如果向
next()
传入参数,会传给上一条执行的 yield语句左边的变量,第一个next()
传参无效 - 如果函数中显式
return
,导致生成器立即变为完成状态,如果return一个值,则作为next()
返回的value
- 调用
js
// MDN上的例子
function *createIterator() {
let first = yield 1;
let second = yield first + 2; // 4 + 2
// first =4 是next(4)将参数赋给上一条的
yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
Promise
- 一种异步编程的解决方案
- 传统的解决方案——回调函数和事件
- Promise对象的特点
- 对象的状态不受外部影响,内部抛出的错误也不会影响到外部
- 支持链式调用
- 一旦状态改变,就不会再变
pending
->fulfilled
pending
->rejected
- 这两种状态一发生,我们就称为
resolved
,如果状态已经改变了,再去添加回调函数,那么也会拿到结果- 与事件的特点不同。事件的特点是,如果你错过了它,再去监听,是得不到结果的
- 无法取消
Promise
,一旦新建它就会立即执行,无法中途取消(缺点) - 当处于
pending
状态时,无法得知目前进展到哪一个阶段(缺点)
详见Promise
async和await
async
就是将函数的返回值使用Promise.resolve()
包裹一层, 和 then
中处理返回值一样,并且 await 只能配套 async 使用
await
就是generator
和Promise
的语法糖,并且内部实现了自动执行generator
- 特点
- 异步终极解决方案
- 优点
- 处理
then
的调用链 - 代码逻辑更清晰,书写更像同步代码
- 处理
- 缺点
- 如果多个异步代码不存在依赖关系,使用了
await
会导致性能上的降低,可以使用Promise.all()
的方式
- 如果多个异步代码不存在依赖关系,使用了
TODO
async和await的实现
定时器函数
常⻅的定时器函数有 setTimeout
、setInterval
、requestAnimationFrame
如果前面的代码 影响了性能,就会导致 setTimeout 不会按期执行。
通常来说不建议使用 setInterval
。
- 它和 setTimeout 一 样,不能保证在预期的时间执行任务
- 它存在执行累积的问题,如果定时器执行过程中出现了耗时操作, 多个回调函数会在耗时操作结束以后同时执行,这样可能就会带来性能上的问题。
requestAnimationFrame
自带函数节流功能,基本可以保证在 16.6 毫秒内只执行一次(不掉帧的情况下),并且该函数的延时效果是精确的,没有其他定时器时间不准的问题
TODO
可以通过requestAnimationFrame
实现 setTimeout
和setInterval