Promise
概述
Promise,要我解释它是个什么东西,我一时间还真的不能好好组织语言,在我的理解里,Promise,字面意思他是一个承诺,而在JavaScript里面他是一种异步操作、任务的解决方案。
同步与异步的理解
同步是一种阻塞的任务,异步是非阻塞式的任务
异步例子:
- 早上起来赶着去上课,买了早饭,可以一边赶路一边吃。
同步例子:
- 早上起来赶着去上课,买了早饭,我们得先赶去教室再吃早饭,也可以先吃早饭再赶去教室。
同步强调赶路和吃早饭这两个任务不能同时进行,在同一时间,要么吃早饭要么赶路。不管是同步还有异步,都是合理的。因为最终都指向了一个结果:到教室上课
显然同步的方式会产生额外的时间消耗,在计算机编程语言里面,显然是不合理的。在计算机的世界中,这样发生阻塞是非常致命的。通常会导致渲染线程(
主线程)阻塞。
界面卡在某一个地方。对于异步任务的执行,现如今使用的最多的方案是多线程和异步。
在JavaScript中使用的是异步的方式进行解决。
Promise
在JavaScript里面,Promise描述了一个异步操作类,他在编程语言里被抽象成一个类,每个实例化的Promise就代表具体的异步操作任务实例。
既然是一个类,我们就可以使用new关键字去实例化一个Promise对象。
1 | const promise = new Promise() //必须传递一个参数,这行代码运行会报错 |
如何描述我们的异步任务?
Promise的构造函数中需要传递一个函数,这个函数中就描述了我们需要进行的异步操作,比如:
1 | const promise = new Promise((resolve, reject) => { |
可以看到这个函数也是可以接收参数的,而这两个参数也是函数类型的参数。分别用于表示异步操作完成了、异步操作失败了。
Promise既然是用来描述一个异步任务的,那么这个异步任务的执行肯定会存在一个状态的,比如:正在执行中、解决了、失败了。无非3种状态。
正在执行中属于pending未决状态,解决了resolve、失败了reject属于结果态,pending一旦变化成为了结果态的某一种,就无非对状态进行变更,结果态中的成功和失败也不允许进行状态变更。
上文的resolve和reject函数就是用于变更状态的,如果在函数中没有调用resolve或者reject,那我们的promise会一直处于一个pending的状态
1 | const promise = new Promise((resolve, reject) => { |
当我们调用了resolve或者reject,promise的状态会立马进行变更。变成结果态,是成功或失败,完全取决于我们在函数里面的逻辑。
举个例子,现在需要写一个函数,通过一个url去动态生成一个img标签,并且当图片加载完成的时候,返回成功,并且在回调里面传输img标签的数据,当失败的时候就传递错误的原因即可。
图片加载,它肯定是一个耗时的操作,我们可以使用Promise对象来对其进行描述。
1 | function createImage(url) { |
回调处理
任务是描述完了,实际开发中我们很少去关注这个任务目前是处于哪一个状态,更多关心的是:这个任务成功的时候我们干嘛,或者出现错误了我们要怎么处理。
比如网络请求。
1 | fetch("https://www.xxx.com/api/getData").then((res) => {/*处理成功后的回调,比如渲染数据到页面上,执行相关的数据清洗操作*/ |
还可以这样写
1 | fetch("https://www.xxx.com/api/getData").then((res) => {/*处理成功后的回调,比如渲染数据到页面上,执行相关的数据清洗操作*/ |
两种写法都可以,第二种写法更加的语义化。
Promise链
实际情况下,我们不可能只做一个异步任务,并且当某个异步任务完成之后,这个异步任务的结果会作为新任务的条件继续去执行新的任务,所以这里就会存在一种任务链的关系。
比如我们学习、考试、上大学
学习学好了才能够在考试中如鱼得水。考试考好了我们就上大学。
在JavaScript里面如何用Promise描述这个过程呢?
1 | function study() { |
promise链的关系如下图
相关理解:
- then方法必定会返回一个新的Promise对象,理解为后续处理也是一个任务
- 新Promise对象的状态取决于后续的处理
- 如果没有相关的后续操作,新Promise的状态和前一个Promise的状态是一致的,数据也和前一个Promise的数据一致。
- 如果又后续的处理但是还没有执行,新Promise对象的状态是pending状态
- 如果后续处理执行了,则会根据后续处理的情况确定新Promise的状态
- 后续处理执行没有错误,新Promise的状态会变更未fulfilled,数据为后续处理的返回值
- 后续处理执行存在错误,新Promise的状态会变更为rejected,数据为异常对象
- 后续执行后返回的是一个Promise对象,则新Promise的状态于这个返回的Promise对象的状态是一致的。
1 | const promise1 = new Promise((resolve) => { |
代码执行,promise1构造函数执行,promise1直接进入了一个fulfilled的状态,并且数据是1
promise2、promise3的状态依赖promise1的回调函数的执行,这个时候回调并未执行,而是放到事件队列当中
主线程空闲,promise1的回调被执行,并且没有错误,打印了数据1,返回了数据2,promise2的回调被执行,打印了数据2,并且没有错误,返回了undefined
静态方法
方法名 | 含义 |
---|---|
Promise.resolve(data) | 直接返回一个完成状态的Promise |
Promise.reject(reason) | 直接返回一个拒绝状态的Promise |
Promise.all(Promise对象数组) | 返回一个Promise,Promise对象数组所有的Promise全部成功则成功,有一个失败就会失败 |
Promise.any(Promise对象数组) | 返回一个Promise,Promise对象数组任意一个Promise对象成功则成功,全部失败则失败 |
Promise.allSettled(Promise对象数组) | 返回一个Promise,Promise对象数组所有Promise对象都是已决状态则成功,这个Promise对象不会失败 |
Promise.race(Promise对象数组) | 返回一个Promise,Promise对象数组任意一个Promise对象已决状态则已决,状态和这个已决的Promise对象的状态一致 |
async和await语法糖
该语法糖在es7的时候提出,主要是为了消除Promise带来的回调写法,更加优雅的表达代码
在没有async和await之前,我们是这样写代码的
1 |
|
有了语法糖,我们可以更好的表示我们的代码,不再产生回调的写法,书写的代码更符合人的逻辑思维习惯
1 | (async () => { |
async必须作用于一个函数,表明了这个函数返回的一定是一个Promise对象,await表示等待这个Promise完成,也就是等待这个Promise从pending的状态变成fulfilled状态