Promise
Promise是个十分优雅的词,他的中文含义是承诺、约定的意思,在古今中外都有非常多的和“承诺”相关的历史故事、寓言故事和名言名句,那么今天我们就来一起了解下JavaScript中的Promise吧!
文章目录
一、Promise基础
promise
是ES6新增的引用类型,可以通过 new 操作符来实例化。创建新的promise需要传入执行器(executor)函数作为参数。
我们new一个Promise实例:
const promise = new Promise(function(resolve, reject) {
resolve(value);
});
Promise
对象有以下两个特点。
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending
(进行中)、fulfilled
(已成功)和rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending
变为fulfilled
和从pending
变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved
(已定型)。如果改变已经发生了,你再对Promise
对象添加回调函数,也会立即得到这个结果。这与事件(Event
)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
特点一: promise
的状态一旦改变就无法更改
我们看一个例子就可以清晰了解到promise
的状态特点:
const p = new Promise((resolve,reject) => {
resolve()
reject()
})
const p2 = new Promise((resolve,reject) => {
reject()
resolve()
})
console.log(p)//Promise {<fulfilled>: undefined}
console.log(p2)//Promise {<rejected>: undefined}
所以我们总结promise
的第一个特点: promise的状态一旦改变就无法更改
特点二:promise
实例内部本身是同步执行的
在初始化promise
时,执行器已经改变了每个promise
的状态。我们知道执行器函数是同步执行的。我看看一个例子:
console.log('a')
setTimeout(function(){
console.log('f')
}, 200)
const p = new Promise(resolve => {
console.log('b')
resolve('g')
console.log('c')
})
setTimeout(function(){
console.log('e')
}, 0)
console.log('d')
//答案:
/*
a
b
c
d
e
f
*/
如果看不懂执行js执行机制可以先阅读我上一篇文章【JavaScript】JS执行机制微任务和宏任务
所以我们总结promise
的第二个特点:promise本身是同步执行的
二、promise
的静态方法
这里先介绍两个最基本的静态方法,下一篇文章会带来:race
、allSettled
、All
、any
的拓展静态方法
Promise.resolve()
通过调用Promise.resolve()方法我们可以实例化一个解决的Promise
下面两个实例的结果实际上是一样的:
const p1 = new Promise(resolve => {
resolve()
})
const p2 = Promise.resolve()
console.log(p1)//Promise {<fulfilled>: undefined}
console.log(p2)//Promise {<fulfilled>: undefined}
console.log(p1==p2)
Promise.reject()
与Promise.resolve()
一样,Promise.reject()
实例化一个拒绝状态的Promise
并抛出错误。
下面两个实例的结果实际上是一样的:
const p1 = new Promise(reject => {
reject()
})
const p2 = Promise.reject()
console.log(p1)//Promise {<rejected>: undefined}
console.log(p2)//Promise {<rejected>: undefined}
三、Promise
的实例方法
1、Promise.then()
Promise.then()
接收最多两个参数:onResolved
和onRejected
。两个参数都是可选的,如果提供的话promise
会进入“fulfilled”和“rejected”状态时执行。
我看来看一下例子:
const p1 = new Promise((resolve,reject )=> {
setTimeout(function(){
resolve('我是resolve,成功状态');
}, 2000);
});
const p2 = new Promise((resolve,reject )=> {
setTimeout(function(){
reject ('我是reject,失败状态');
}, 2000);
});
p1.then((res)=>{ console.log(res)},(err)=>{ console.log(err)}) //我是resolve,成功状态
p2.then(null,(err)=>{ console.log(err)})// 我是reject,失败状态
因为期约状态只有一次所以onResolved
和onRejected
这两个操作一定是互斥的。
传给.then()的任何非函数类型的参数都会被静默忽略。所以我们一般只提供一个参数,那么在另一个状态的参数上我们传入null。可以有助于避免在内存中创建多余的对象。
我们根据特性:传给.then()的任何非函数类型的参数都会被静默忽略做一道习题:
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
//第一个then和第二个then中传入的都不是函数,一个是数字,一个是对象,因此发生了透传,将resolve(1) 的值直接传到最后一个then里。
所以输出结果为:1
2、Promise.catch()
catch方法用于给Promise
添加拒绝处理程序。这个方法只接收一个参数,事实上他就是.then的一个语法糖,调用它就相当于上文中的:
Promise.then(null,(err)=>{ console.log(err)})
根据(事实上他就是.then的一个语法糖)这一个特点,我们再来结合Promise特点一(promise的状态一旦改变就无法更改) 和 .then的特点(Promise.then()
接收最多两个参数:onResolved和onRejected)做一道综合练习题:
Promise.reject('err!!!')
.then((res) => {
console.log('success', res)
}, (err) => {
console.log('error', err)
}).catch(err => {
console.log('catch', err)
})
// 在这道题中,错误直接被then的第二个参数捕获了,所以就不会被catch捕获了,输出结果为:'error' 'error!!!'
3、Promise.finally()
finally()
方法用于指定不管 Promise
对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
promise.then((result) => {/* ···*/}) .catch((error) => {/* ···*/}) .finally(() => {/* ···*/});
下面是一个例子,服务器使用 Promise 处理请求,然后使用finally方法关掉服务器。
server.listen(port) .then(function() { /* ... */ }) .finally(server.stop);
finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
我们来看一道习题了解finally的特性:
Promise.resolve('1')
.then(res => {
console.log(res)
})
.finally(() => {
console.log('finally')
})
Promise.resolve('2')
.finally(() => {
console.log('finally2')
return '我是finally2返回的值'
})
.then(res => {
console.log('finally2后面的then函数', res)
})
/*
结果:
1
finally2
finally
finally2后面的then函数 2
*/
finally本质上是then方法的特例。
总结
那么最后总结一下 promise
的几个小特点:
- 特点一:
promise
的状态一旦改变就无法更改 - 特点二:
promise
实例内部本身是同步执行的 - 特点三:
Promise.then()
接收最多两个参数:onResolved
和onRejected
。两个参数都是可选的,如果提供参数的话promise
会进入“fulfilled”和“rejected”状态时执行。 - 特点四:传给
.then()
的任何非函数类型的参数都会被静默忽略 - 特点五:
Promise.catch()
只接收一个参数。事实上他就是.then的一个语法糖 相当于:Promise.then(null,(err)=>{ console.log(err)})
只是我们不用传递第一个null参数了 - 特点六:
Promise.finally()
方法不管Promise对象最后的状态如何都会执行 - 特点七:
finally()
方法的回调函数不接受任何的参数,也就是说你在.finally()函数中是无法知道Promise最终的状态是resolved还是rejected的 - 特点八:
finally()
它最终返回的默认会是一个上一次的Promise
对象值,不过如果抛出的是一个异常则返回异常的Promise
对象。