前言
异步问题是我们在学习JavaScript过程中需要关注的一个重点。但是据我观察,目前的各种资料还是以介绍异步问题的解决方案(即,期约、异步函数等异步编程方案)为主,对于异步问题本身讨论较少。所以在这里我尝试按照自己的理解对异步问题做一个提纲挈领的总结,方便自己后续更好的去学习理解各种异步编程方案。
一、异步问题
1.什么是异步问题?
异步问题,即 同步任务依赖异步资源的问题。
由于JS的单线程事件循环机制,导致程序中的同步任务一定是在异步任务之前执行的。但是在某些情况下,某些同步任务又依赖于异步任务中所"生产"的资源,其需要在某些异步任务之后执行,因此就与JS本身的机制产生了冲突。这种冲突就是异步问题 。解决冲突的方案就是异步编程方案。
2.异步编程方案与"逆异步"
(1) 异步编程方案所实现的效果
异步编程方案可以帮助我们解决异步问题。也就是说通过异步编程方案,我可以实现如下的效果:
当某个异步任务执行完毕后,可以"通知"另一个同步任务执行,并且这个同步任务可以使用异步任务所生产的资源。
(2) 常用的异步编程方案
- 回调函数
- 期约Promise
- 异步函数 async/await
(3) 什么是逆异步?
"逆异步" 是我自己总结的一个概念 , 它其实就是上面写到的"异步编程方案所实现的效果"。
二、异步场景
1.什么是异步场景?
异步场景就是指我在实际工作中会面临异步问题的场景,或者说在实际工作中需要实现"逆异步"的场景。更准确的说异步场景就是异步资源传递的场景。
根据实际工作中的经验,我总结了异步场景的核心概念,与异步场景的几个常用范式,后面我会 一 一 介绍。
2.异步场景的核心概念
异步场景的核心概念我总结为"两角色,一核心"
(1)两角色------生产者和消费者
在异步场景中有两种角色:
- 异步资源生产者 , 指能够获取延迟资源的异步任务
- 异步资源消费者, 指依赖延迟资源的任务
(2)一核心------ 同步与异步的连锁
连锁是指 生产者获取到异步资源后,通知消费者使用的机制。连锁是连接同步代码与异步代码的桥梁,也是所以异步编程方案所要解决的核心问题。
3.常见的异步场景
(1)基础场景
基础场景就是有一个生产者和一个消费者,当生产者获取到异步资源后传递给消费者,消费者消费异步资源。
基础场景是所有异步场景的基础,也是实际工作中最常遇到的情况。例如:需要将请求到的某个数据打印出来:
JavaScript
let r = await fetch('/bar');
console.log(r);
在上面的这个案例中 fetch('/bar')
就是生产者;console.log(r)
就是消费者 ;变量 r
就是异步资源;
而 fetch
前的await
关键字就可以看做是"连锁机制",它代表这里使用了 "异步函数 async/await" 这一异步编程方案。
(2)失败处理场景
失败处理场景则是指这样一种情况,生产者生产资源并不一定会成功,有时也会出现生产失败的情况,这时候就需要有两个消费者,一个是生产成功时的消费者,一个是生产失败时的消费者。
这个时候可能有一个疑问了,既然都生产失败了,那么就表示原本应该产出的资源不存在了,那么失败的消费者又能消费什么呢?
实际上生产失败不代表没有产出,例如生产失败了会抛出错误,这个错误就是失败时的资源,甚至生产失败这个消息本身就是一种资源。
示例:
需要将请求到的数据打印出来,但是请求有可能失败,失败的时候会返回失败原因,此时需要将失败原因打印出来。
JavaScript
try{
let r = await fetch('/bar');
console.log(r);
}catch(e){
console.log(e)
}
在上面的示例中console.log(r)
就是生产成功时的消费者;console.log(e)
就是生产失败时的消费者。变量r
是生产成功时产出的资源;变量e
则是生产失败时产出的资源。
(3)异步任务串行场景
异步任务串行场景是指,一个生产者在进行资源生产者需要依赖另一个生产者所生产的资源。即一个对象身上同时具备生产者和消费者两种角色的情况。
示例:
现在需要请求数据B,然后将数据B打印出来,但是在请求数据B时又必须以数据A作为参数,因此就需要先请求数据A,在获取到数据A后再以其为参数请求数据B,最后再打印数据B。
JavaScript
let RA = await fetch('/bar');
let RB = await fetch(`/foo?value=${RA}`);
console.log(RB);
在上面的示例中fetch('/bar')
就是生产者A;fetch(`/foo?value=${RA}`)
就是生产者B(实际也是消费者);console.log(RB)
就是与生产者B关联的消费者。变量RA
是资源A;变量RB
是资源B。
(4)异步任务组合场景
异步任务组合场景就是指一个消费者需要消费几个生产者所生产的资源的情况。
示例:
现在需要请求A、B、C三个数据,当这三个数据都请求到了之后再打印一句话"数据初始化完成"。
JavaScript
await Promise.all([
fetch('/bar'),
fetch('/foo'),
fetch('/dialog')
]);
console.log('数据初始化完成');