一文搞懂Promise

Promise在我们平常的开发当中还是经常遇到的, 前两天写的一篇文章中, 最后使用map+promise.all()解决当时遇到的一个问题, 粉丝也是后台留言, 可不可以写一篇Promise的文章

那么我们今天就来详细的讲解一下Promise, Promise其实是es6中引入的进行异步编程的一种新的解决方案, 从基本语法来说, 它其实就是一个构造函数, 可以用它来封装异步的任务, 并对结果进行处理, 大家估计都熟悉回调地狱这个概念, 其实Promise最大的好处就是解决回调地狱的问题

那么我们上面提到了回调地狱, 顺便介绍一下回调地狱

在我们旧的方案中, 只是单纯的使用回调函数

那么回调地狱的问题, 就是回调函数的嵌套调用, 外部函数的异步执行结果是嵌套回调函数执行的条件, 提到了嵌套, 缺点就很明显了, 不利于我们代码的阅读和异常处理及日常代码维护

我们写一个代码, 大家就明白回调地狱了

图片

我们会发现, 里面嵌套的层级会很深, 以上就是一个回调地狱问题非常典型的一个情景

那么我们使用Promise来做一个小案例, 让大家先来了解一下Promise的基本使用

图片

在上面的案例中, 我们使用Promise去封装fs读取文件的操作, 接下来, 我们详细的去介绍一下Promise的使用

还是根据上面的案例, 我们在成功之后, 调用了resolve方法, 将Promise的状态改为了成功, 那么这个状态到底是什么, 我们如何查看呢?

其实这个状态就是Promise实例对象中的一个属性: PromiseState

我们打印一下Promise对象

图片

我们打印出来, 就可以看到这个状态了, 该状态主要有三个值分别是初始化时的pending, 还有成功时的resolved和fullfilled和失败时的rejected。

在该三种状态变化时只有两种情况, 分别是pending变为resolved和pending变为rejected, 一个Promise对象只能改变一次, 无论是成功还是失败, 都会返回一个结果数据

Promise的状态我们明白之后, 我们就看一下Promise对象的值

图片

我们直接在控制台打印出来, Promise对象的值其实就是PromiseResult, 该属性中保存的是异步任务成功或者失败的结果, 我们不难发现, 我们resolve了一个true, 该值就是true, 那么我们直接reject一个false, 该值就变成了false, 所以, 在Promise中只能是resolve和reject去修改实例对象的值

在了解完实例对象的状态和值之后, 我们大致知道Promise的如何工作的, 它的流程是什么样子的?

我用图片给大家展示一下

图片

我们还是以读取文件那个为例, 就可以看到Promise的大致流程啦

图片

上面图上显示最后返回一个新的Promise对象, 这里说的返回是then方法的返回结果是一个新的Promise对象

通过上面介绍, 我们Promise大致的工作流程基本就可以掌握了, 接下来, 我们深度看一个Promise这个构造函数

大家也可以通过MDN文档, 详细的看一下Promise这个js内置构造函数

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise

在上述案例中, 我们可以看到在new Promise()的时候, 需要传入一个回调函数, 这个回调就是一个执行器函数, 在回调中需要接受两个参数, 一个是resolve函数和reject函数, 这两个函数分别是内部定义的成功时或者失败时我们需要调用的函数

我们写一个小案例看一下执行器函数

图片

被选中的部分就是执行器函数, 该执行器函数是在Promise内部同步调用的, 就是在代码执行到这一块时, 我们上面被选中的代码会被立即执行, 我们不妨打印一下

图片

上面的图片案例, 就印证了, 在代码执行到这一块时, 执行器函数会立即执行

接下来我们说一个then方法, then方法其实就是用来指定回调的, 它也接收两个参数, 分别是onResolved成功时的回调和onRejected失败时的回调, 返回呢是一个新的Promise对象

图片

还有一个catch方法, 也是用来指定回调的, 不过catch方法只能用来指定失败时的回调, 其实catch也是做了一个单独的封装, 内部的实现也是由then方法来实现的

图片

catch内部的实现怎么是由then方法来实现的呢?

图片

我们打开MDN文档, 就可以看到catch方法是Promise.prototype.then(undefinde, onRejected)的一种简写形式

在MDN文档关于Promise部分, 我们可以看到方法还是挺多的

图片

接下来, 我们再介绍一些API

先来介绍一下Promise.resolve()方法

该方法接收一个成功的数据或者promise对象, 返回一个成功或者失败的promise对象

图片

我们发现, 返回的结果是一个成功的Promise, 值是我们传入的数字, 那如果我传入一个参数是Promise对象, 又会返回什么呢?

图片

根据上图, 我们可以得到如下信息, 如果我们传入一个Promise对象, 那我传入的参数结果就决定了我们resolve方法的结果, 如果是一个非Promise类型的对象, 则会返回一个成功Promise对象

我们再来介绍一个Promise.reject()方法

该方法接收一个失败的原因, 返回一个失败的promise对象

该方法和我们上面写的resolve方法都是属于Promise这个函数对象的, 不属于实例对象的方法

我们来写一个小例子

图片

该方法无论我们传入什么类型的数值, 都会返回给我们一个失败的Promise对象

接下来, 我们要说一下Promise.all()方法, 该方法呢, 接收n个Promise数组, 返回一个新的Promise, 只有所有的Promise都成功, 才会返回成功的Promise对象, 但凡有一个失败, 就会直接失败

大家也可以看看之前写的文章, 就是使用map+promise.all()解决的

for循环和forEach性能上及异步同步化写法的差异

接下来, 我们来写一个小例子, 演示一下Promise.all()方法的使用

图片

如果有一个失败了, 那么返回的应该是失败的那个Promise的结果

图片

大概常用的这几个API我们就介绍完毕了, 随后, 我们再说一下async和await

大家可以查看MDN文档, 详看async函数

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function

async函数的返回值也是一个Promise对象, 而Promise对象的结果是由async函数执行的返回值决定的, 我们来写个例子

图片

我们发现确实返回一个Promise对象, 我们函数中什么都没有return, 所以值是undefined, 那么我们return一个非Promise类型的数值, 我们看看返回的是什么

图片

返回的是一个成功的Promise对象, 值是我们return的值

那么我们return一个Promise类型的值, 我们看看结果会不会发生改变

图片

结果是一个失败的Promise对象, 值为我们reject传入的值

那么我们抛出错误呢, 结果其实也是根据我们函数返回值决定的

接下来, 我们介绍一个await表达式

大家也可以查看MDN文档, 详看await表达式

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await

await右侧的表达式一般是Promise对象, 但是呢也可以是其他值, 如果右侧是一个Promise对象的话, await会返回Promise成功的值, 如果右侧是其他值, 那么会将此值作为await的返回值

我们先来验证一下其他值, 我们看看是不是作为了await的返回值

图片

接下来, 我们在函数中定义一个Promise对象, 看看打印的是不是Promise成功的值

图片

那么我们调用reject方法, 看看结果如何

图片

结果抛出异常了, 我们使用try…catch捕获处理一下

图片

但是需要注意的是, await必须写在async函数中, 但是async函数中可以没有await

在日常开发中, 我们经常会基于Promise进行请求函数的封装

大家可以详看如下文章

uni-app网络请求封装

对于Promise的介绍, 我们大致讲解到这里

后面会给大家做一个Promise的注意点

在开发中经常会遇到的一些问题

我们下期见吧

声明:文中观点不代表本站立场。本文传送门:https://eyangzhen.com/389199.html

联系我们
联系我们
分享本页
返回顶部