1.并发,并行,异步,同步
1.1并发,并行
并发:对于单核处理器来说,计算机通过分配时间片的形式,让一个任务执行一段时间后执行另一个任务,不同任务会交替往复的进行。(这个过程也被称为进程或者线程的上下文切换)
并行:对于多核处理器来说,计算机能够在不同的核心上并行的执行任务。
1.2同步,异步
同步和异步是两种不同的编程方式。
同步:必须在前一个任务完成之后,才能执行下一个任务,所以在同步中没有并发和并行的概念。
异步:不同的任务之间不会相互等待,在执行任务1时可以同时执行任务2,可通过多线程编程实现,每个线程会被分配到不同的核心上执行,实现真正的并行。但如果计算机为单核处理器,操作系统则会通过分配时间片的方式并发执行这些线程。
2.js单线程异步编程
异步编程:允许我们在执行一个长时间任务时,程序不需要进行等待,而是继续执行之后的代码,知道这个任务完成之后在通知你,形式通常为回调函数。
js是没有多线程概念的,但是可以通过函数回调机制实现单线程的并发,当回调函数需要被执行时,会先执行回调函数,通过这种异步编程的方式实现单线程的并发。
优点:避免程序阻塞,提高CPU的执行效率
对于I/O密集的程序(WEB应用)会经常执行网络操作数据库访问,这类应用适合
异步编程。如果用多线程容易浪费资源,因为每个线程的大多数时间都是在等待I/O操作,线程自身也会占用内存,线程的切换也有额外开销,线程之间还有资源竞争问题。
多线程编程适合于计算量密集的应用(视频图像处理,科学计算),尽可能让每一个核心发挥最大功效
3.Promise
当需要依次执行多个异步操作时,我们我们需要嵌套回调函数,导致“回调地狱”,代码可读性差。
使用Promise可以用链式结构将多个异步操作串联起来。在链式结构的后面加上.catch
方法可以接受,链式中任意个promise的错误,.finally
方法可以在promise结束后做一些清理工作。
4.async,await
async与await是ECMA17新加入的语法糖,可以让异步操作更加简单明了。
首先用async将一个函数标记为异步函数,在异步函数中我们可以调用其他的异步函数,使用await接收promise完成后返回的结果。awati不会暂停程序执行,它的底层是通过Promise和事件循环实现的。
5.await使用时的陷阱
陷阱1
async function f(){
const a = await fetch('http://...post1);
const b = await fetch('http://...post2);
//修改
const promiseA = fetch('http://...post1);
const promiseB = fetch('http://...post2);
const [a,b] = await Promise.all([promiseA,promiseB]);
这样的写法,会打破两个fetch的并行,需等到第一个任务完成后才开始执行第二个任务,我们可以用.all
将promise组合起来,然后再用await,这样程序的运行效率能够提升一倍.
陷阱2
async function f() {
[1, 2, 3].forEach(async (i) => {
await someAsyncOperation();
})
}
//修改
const promises = [
someAsyncOperation(),
someAsyncOperation(),
someAsyncOperation(),
];
for await (let result of promises) {
//...
}
f();
在循环中使用异步操作是不能使用forEach
或者map
这一类方法的,如上面代码中,forEach
会立刻返回,不会等到所有异步操作都执行完毕。可以使用传统的for
循环解决。
陷阱3
不能在全局或普通函数中使用await
,其只能被使用在异步函数中(async function
)
6.promise常见的API
6.1 Promise构造函数:Promise(excutor){}
excutor函数:同步执行 (resolve, reject) => {}
resolve函数:内部定义成功时我们调用的函数 value =>{}
reject函数:内部定义失败时我们调用的函数 reason => {}
说明:excutor会在Promise内部立即同步回调,异步操作在执行器中执行
6.2 Promise.prototype.then方法:(onResolved, onRejected) => {}
onResolved函数:成功的回调函数 (value)=> {}
onRejected函数:失败的回调函数(reason) => {}
说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调
返回一个新的promise对象
6.3 Promise.prototype.catch方法:(onRejected) => {}
onRejected函数:失败的回调函数(reason)=>{}
说明:then()的语法糖,相当于:then(undefined,onRejected)
6.4 Promise.resolve方法:(value)=> {}
value:成功的数据或promise对象
说明:返回一个成功/失败的promise对象
6.5 Promise.reject方法:(reason) => {}
reason:失败的原因
说明:返回一个失败的promise对象
6.6 Promise.all(iterable)方法:(promises) =>{}
promises:包含n个promise的数组
说明:这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。
6.7 Promise.race方法:(promises)=> {}
promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
看起来race方法似乎没什么特别的用处,但在处理Web服务器中的超时逻辑时却十分方便,例如我们为一个 Promise(可能是一个数据库操作)定义了100ms 的执行时限,如果耗时超过这个时间就返回一个超时错误,在这种情况下就可以考虑使用race方法。