本文章旨在梳理JavaScript异步编程知识点,大多引用自《JavaScript高级程序设计》(第4版)。
前端学习:异步编程(Promise和异步函数)
异步编程
JS的单线程和异步编程
为什么javaScript是单线程的,却可以处理异步函数呢?
这个问题的答案很简单,JS是单线程事件循环模型,所有的同步任务放在主线程(或称为同步线程)的执行栈里,按照先进先出的顺序在主线程执行,当出现一个异步任务时,浏览器创建一个异步线程来执行异步函数,并将回调函数放入任务列表中,当主线程执行完当前执行栈中的任务后,再顺序执行回调函数任务列表里的任务。
因此虽然JavaScript是单线程的,但异步任务仍然是多线程完成的,且异步任务的回调函数又是由主线程来完成的。
回调地狱
以往的JS中,支持定义回调函数的方式来表示异步函数的完成,此时如果想要串联多个异步操作,就要在回调函数里嵌套异步任务再嵌套回调函数。。。从而产生了回调地狱。
回调地狱的解决方法——ES6的新引用类型:Promise类型
Promise
什么是Promise?Promise有哪些状态?
- Promise是ES6的新引用类型,可以更加优雅的组织异步逻辑。
- Promise有三种状态:pending、resolved和rejected。
- Promise的状态不可逆,Promise的落定状态一旦确定,就是不可逆转的。
- Promise的两大用途:
(1)抽象的表示一个异步操作,Promise的状态表示异步操作是否完成。即告诉我们一个异步操作是否完成。
(2)Promise封装的异步操作会实际生成某个值,这个值可以被我们访问到。
Promise有哪些实例方法?(then、catch、finally)
- 为什么Promise可以使用then来实现链式调用?
then方法实现了一个Thenable接口,每个then方法返回一个新的Promise实例,这个新的Promise又可以继续调用then方法。 - then方法放入的两个参数,第一个是onResolved处理程序,第二个是onRejected处理程序,两个参数互斥。
- onRejected处理程序用于捕获异步错误的任务,由于捕获错误成功(任务执行成功),那么它返回一个resolved的Promise。(注意onResolved处理程序抛出异常则返回一个rejected的Promise,但却会把错误对象包装在一个resolved的Promise中返回)
- catch方法放入的是onRejected处理程序。
- finally方法不管Promise的状态是resolved还是rejected都会执行,一般用于添加清理代码。
非重入特性
当Promise的状态确定以后,与它相关的处理程序不会立即执行,而是放到之前所说的任务队列里,等待主线程执行完所有目前的同步任务后才会执行(会按照添加他们的顺序依次执行)。
Promise的链式调用
- Promise的链式调用目的是串联化异步任务,原理是每一个后续处理程序都会等待前一个Promise解决,然后实例化一个新的Promise并返回。
- Promise的链式调用解决了回调地狱的问题。
Promise的静态方法:Promise.all()、Promise.race()
Promise.all()
- Promise.all()接收一个可迭代对象,返回一个新的Promise(注意即使所有期约都成功也返回的是一个Promise,这个新的Promise的解决值是包含所有Promise解决值的数组)。
- 如果至少有一个包含的Promise待定,那么合成的Promise也待定(pending),如果有一个Promise拒绝,那合成的Promise也拒绝(rejected)
- 只有所有Promise都成功解决,那合成期约的解决值是包含所有Promise解决值的数组(数组顺序按照迭代器的顺序)
- 如果有Promise拒绝,那么第一个拒绝的Promise会将自己的拒绝理由作为合成期约的拒绝理由。
Promise.race()
- Promise.race()接收一个可迭代对象,返回一个新的Promise。
- 返回一组集合中最先解决或拒绝(即最先落定)的Promise的镜像。
- 不管是all还是race,合成期约会静默处理所有包含期约的拒绝操作,不会有错误跑掉。
Promise的应用
函数合成
将多个函数合成一个函数,利用链式调用的参数传递。
Promise的封装(实现一个Promisify函数)
将原本需要通过传入回调参数来实现回调执行(或者叫同步执行)改为利用promise的.then的方式来调用,从而实现逻辑上的同步操作。
重点:参数传递
function promisify(func) {
return function (...args)