ES6 - Promise
什么是 Promise?
Promise 是异步编程的一种解决方案,简单来说,就是一个容器,里面保存着一个某个未来才会结束的事件,从语法上说,Promise 是一个对象,从它可以获取异步操作的消息, Promise提供统一的 API,各种异步操作都可以用同样的方法进行处理。
特点:
1,对象的状态不受外界影响
2,一旦状态受改变,就不会改变
基础用法
创建 Promise 实例:
const promise = new Promise(function(resolve,reject){
// this is your code
if(/*异步操作成功*/) {
return resolve(value);
}else {
return reject(error);
}
});
resolve 函数作用:
把 Promise 对象状态变为 成功,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。
reject 函数作用:
把 Promise 对象状态变为失败,在异步操作失败时调用,并将异步操作的结果做为参数传递出去。
这里提示: 在 Promise 实例中,调用 resolve 或者 reject 之后并不会打断后续代码的执行,所以这里我们推荐使用 "return resolve()/return reject()" 直接把 resolve/reject 返回,而不是直接 resolve()/reject() 这样可以保证 resolve() / reject() 后面的代码不会执行。
在 Promise 实例生成之后可以使用 .then 方法指定 resolved 和 rejected 的回调函数:
promise.then(function(value){
// 异步操作成功之后的 code
},function(error){
// 异步操作失败之后的 code
});
Promise 实例的几种特别情况
1,resolve 返回的是另一个 promise 实例
const p1 = new Promise(function(resolve,reject){
return resolve();
})
const p2 = new Promise(function(resolve,reject){
return resolve(p1);
})
p2.then(funciton(vlaue){
console.log('p1 成功了');
},function(error){
console.log('p2 失败了')
})
这里 我们 p2 这个 promise 实例中 resolve() 返回的是 p1,另一个 promise 实例,这时决定 p2 的回调成功方法是否会被调用的条件就是 p1 的回调结果了,如果 p1 成功 ,那么 p2 的回调会被立即执行,也就是 打印 ‘p1 成功’,否则执行 'p2' 失败。
.catch() 方法
Promise.prototype.catch
方法是.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
const promise = new Promise(function(resolve,reject){
if(false == true){
console.log(x + 2); // 这里有错误,x 未定义
return resolve(value);
}else {
return reject(error);
}
});
promise.then(function(value){
console.log('成功!');
}).catch(function(error){
console.log(error)
})
这里我们推荐用 .catch() 方法代替 .then(f1(){},f2(){}) 中的 f2() 也就是 reject() 的回调方法,因为 .catch() 不光可以代替 f2 ,还可以捕捉到 promise 中的错误,也就是说,x 未定义这个错误 .catch() 方法是可以捕捉到的,而 f2() 是做不到的,而且这种写法更像同步的代码逻辑。
.finally() 方法
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
prmise
.then(res=>{})
.catch(error=>{})
.finally(()=>{});
这里需要注意 finally 方法是无法接收参数的,也就是说它无法判断当前 prmise 实例的最终状态是 resolve 还是 reject ,适合把一些在 resolve 和 reject 回调方法里面都需要执行的代码提取出来,放到 finally 里面中,这样就可以减少代码行数。
.all() 方法
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1,p2,p3]);
其中,p1,p2,p3 都是 Promise 实例,然后 p 的状态由 p1,p2,p3 决定,有两种情况:
1,p1,p2,p3 全部 resolved 这样 p 才可以 resolved 然后 p1,p2,p3 返回的值组成一个数组返回给 p 的回调函数。
2,p1,p2,p3 中一旦有一个或者多个 rejected 那么 p 的状态也变为 rejected 然后第一个 rejected 实例把返回的值
给 p 的回调函数。
总结起来就像事务的原理一样,.all() 方法具有原子性,要么都成功,要么失败。
这里需要注意,如果 .all() 中的 promise 实例某一个有自己的 .catch() 方法,并且这个 .catch() 返回的又是一个 promise 实例,这时候,这个 promise 对象如果 rejected 的话,它的最终状态是由它的 .catch() 中的这个 promise 实例的状态决定。
const p1 = new Promise((resolve,reject)=>{
return reject(erro);
}).catch((erro)=>{
return Promise((resolve,reject)=>{
return resolve();
})
})
const p2 = new Promise((resolve,reject)=>{
resolve()
})
const p = Promise.all([p1,p2]); // 这里 p1 的状态由 .catch 中的 Promise 决定
p.then((value)=>{
console.log('success')
}).catch((error)=>{
console.log(error)
})
.race() 方法
Promise.race
方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1,p2,p3]);
.race() 方法与 .all() 方法不同的是,.race() 是根据 [p1,p2,p3,........] 实例数组中第一个发生变化的 promise 实例状态所决定的,假设 p1 实例先返回一个 resolved 状态,或者 rejected 状态,那么 p 的状态也会发生变化,和 p1 的状态相同,然后调用对应的回调函数。
.resolve() 方法
有时需要将现有对象转为 Promise 对象,Promise.resolve
方法就起到这个作用。
const p = Promise.resolve('foo');
这里 resolve() 的参数有三种情况:
1,Promise 实例
如果参数是一个 promise 实例,那么 resolve 将不会做任何操作,会直接把 promise 实例返回。
2,有 then() 方法的对象
let obj = {
then: function(resolve,reject) {
resolve(1);
}
}
// 这里 Obj 就是带有 then() 方法的对象
const p = Promise.resolve(obj);
p.then(function(value){
console.log(value); // 这里 vlaue 的值是 1
})
// 这里 Promise.resolve() 会立即执行 obj 的 then 方法,然后状态更改为 resolved,然后直接调用 p 的 then 方法打印 value 的值
3,没有 then() 方法的对象或者不是对象
const p = Promise.resolve('hello world!');
p.then((value)=>{
console.log(value); // value = hello world!
})
这里 resolve() 方法的参数是 ‘hello world!’ 这个字符串,不是带有 then 方法的对象,所以直接转换成一个 promise 实例,并且这个 promis 实例的状态是 resolved ,所以直接执行 p.then() 方法,'hello world!' 会做为参数传送给 p.then() 方法。
4,不带有任何参数
const p = Promise.resolve();
p.then(()=>{})
如果 resolve() 方法中没有传递任何参数的话,那么将会直接返回一个 resolved 状态的 promise 实例
.reject() 方法
Promise.reject(reason)
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
。
const p = Promise.reject('出错了!');
p.catch((error)=>{
console.log(error); // 出错了!
})
这里 reject() 方法的参数会原封不动的传递出去,而不会做什么改变,这与 resolve() 方法不同,例如:
let p = {
then: function(resolve,reject) {
reject('出错了!')
}
}
const p1 = Promise.reject(p);
p1.catch((error)=>{
console.log(error); // 这里 error 是 p 这个对象,而不是 '出错了' 这个参数
})
.try() 方法
在项目开发中,我们可能会碰到这样的情况,不管是同步的代码还是异步的代码都想用 Promise 封装起来,但是这样会出现一些问题,例如:
let f = function(){
console.log('now');
}
Promise.resolve().then(f);
console.log('next');
// 执行顺序 先 next 后 now,这是因为放到 promise 里面之后就成了异步操作,会在本轮事件的末尾执行,但如果这是一个同步方法,这显然是不合理的
让我们使用 try() 改造一下:
let f = () => console.log('now');
Promise.try(f);
cosolog.log('next');
// 执行顺序,now,next
这样同步方法也会同步执行。