前言
Promise是异步编程的一种解决方案,可以解决回调地狱。
优点:
- 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
- 提供统一的接口,使得控制异步操作更加容易。不需要再自己封装,同时提高代码阅读的效率。
缺点:
- 无法取消
Promise,一旦新建它就会立即执行,无法中途取消。 - 如果不设置回调函数,
Promise内部抛出的错误,不会反应到外部。 - 当处于
pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
一、基本使用
1.立即执行
在通过new创建Promise对象时,需要传入一个回调函数,称之为executor。
- 这个回调函数会
立即被执行,并且给传入另外两个回调函数resolve、reject; - 当调用
resolve回调函数时,会执行Promise对象的then方法传入的回调函数; - 当调用
reject回调函数时,会执行Promise对象的catch方法传入的回调函数。
const promise = new Promise(() => {
console.log('被立即执行');
})
// 被立即执行
2.then/catch
then方法传入的两个回调函数:第一个回调函数会在Promise执行resolve时回调,第二个回调函数会在Promise执行reject时回调。
// 4.then方法有两个参数
function foo () {
return new Promise((resolve, reject) => {
// resolve(200)
reject(404)
})
}
const promise = foo()
promise.then((res) => {
console.log(res);
}, (err) => {
console.log(err);
})
// 等价于
// promise.then((res) => {
// console.log(res);
// }).catch((err) => {
// console.log(err);
// })
二、三种状态
状态一旦确定下来,就不可更改(锁定)。
- 待定(
pending):初始状态,既没有被兑现,也没有被拒绝;- 当执行
executor中的代码时,处于该状态;
- 当执行
- 已兑现(
fulfilled):意味着操作成功完成;- 执行了
resolve时,处于该状态;
- 执行了
- 已拒绝(
rejected):意味着操作失败。- 执行了
reject时,处于该状态。
- 执行了
所以在executor中先resolve再reject或者先reject再resolve,后面的处理都是无效的。
new Promise((resolve, reject) => {
resolve(200)
// 以下代码无效
reject(404)
}).then((res) => {
console.log(res, 'then---');
}).catch((err) => {
console.log(err, 'err---');
})
// 200
三、then方法
- 同一个
Promise可以被多次调用then方法。当resolve方法被回调时,所有的then方法传入的回调函数都会被调用。
function foo () {
return new Promise((resolve, reject) => {
resolve(200)
})
}
const promise = foo()
promise.then((res) => {
console.log(res);
})
promise.then((res) => {
console.log(res);
})
promise.then((res) => {
console.log(res);
})
// 200
// 200
// 200
then方法传入的“回调函数”,可以有返回值。then方法本身有返回值,是一个Promise。
- 如果返回的是普通值(数字/字符串/普通对象/undefined),那么普通值被作为一个新的
Promise的resolve值。
Promise中then的链式调用原理:前一个then的返回值返回给后一个then。
function foo () {
return new Promise((resolve, reject) => {
resolve(200)
})
}
const promise = foo()
const result = promise.then((res) => {
return 'aa'
})
// 等价于
const result1 = promise.then((res) => {
return new Promise((resolve, reject) => {
resolve('aa')
})
})
console.log(result === result)
// true
- 如果返回的是一个
Promise,then/catch之后的状态取决于返回的Promise的状态。
function foo () {
return new Promise((resolve, reject) => {
resolve(200)
})
}
const promise = foo()
promise.then((res) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(202)
}, 3000)
})
}).then((res) => {
console.log(res);
})
// 202
- 如果返回的是一个对象,并且该对象实现了thenable
function foo () {
return new Promise((resolve, reject) => {
resolve(200)
})
}
const promise = foo()
promise.then((res) => {
// 这里的return相当于new Promise(resolve => resolve(obj.then))
// 所以将obj中then函数内的状态返回给下一个then
return {
then: function (resolve, reject) {
resolve(204)
}
}
}).then((res) => {
console.log(res);
})
// 204
三、catch方法
- 可以捕获的异常:
reject()throw new Error()
function foo () {
return new Promise((resolve, reject) => {
resolve(200)
})
}
const promise = foo()
promise.then((res) => {
return new Promise((resolve, reject) => {
// reject(404)
throw new Error(405)
})
}).catch((err) => {
console.log(err);
})
// Error: 405
// ......
Promise中catch的链式调用原理:依次从头捕获调用链的异常,不一定是上一个then的异常。
const promise = new Promise((resolve, reject) => {
// 异常1
// reject(404)
resolve(200)
})
promise.then((res) => {
}).then((res) => {
// 异常2
throw new Error(405)
}).catch((err) => {
console.log(err);
})
// 405
catch方法也有返回值,是一个Promise。
const promise = new Promise((resolve, reject) => {
reject(404)
})
promise.then((res) => {
}).catch((err) => {
// return '11'
// 等价于
return new Promise((resolve, reject) => {
resolve('11')
})
}).then((res) => {
console.log(res, 'res---then2');
}).catch((err) => {
console.log(err, 'err---catch2');
})
// 11 res---then2
四、finally方法
- 无论
Promise对象变成fulfilled还是rejected状态,最终都会执行finally代码。 - 不接收参数。所以无法得知前一步是
fulfilled还是rejected状态。
const promise = new Promise((resolve, reject) => {
reject(404)
})
promise.then((res) => {
return '200'
}).catch((err) => {
return err
}).finally((res) => {
console.log(res, 'finally');
})
// undefined finally
五、类方法-resolve/reject
Promise.resolve()与then方法传入不同类型的值现象一致。
// const promise = Promise.resolve({ name: 'resolve' })
// 等价于
const promise = new Promise((resolve, reject) => {
resolve({name: 'resolve'})
})
promise.then((res) => {
console.log(res);
})
// { name: 'resolve' }
Promise.reject()
传入什么值就返回什么值。
const promise = Promise.reject({ name: 'reject' })
// 等价于
// const promise = new Promise((resolve, reject) => {
// reject({name: 'reject'})
// })
promise.then((res) => {
}).catch(err => {
console.log(err);
})
// { name: 'reject' }
六、类方法-all/allSettled
Promise.all()
所有的Promise都变成fulfilled状态时,才拿到结果。
const p1 = Promise.resolve(202)
const p2 = Promise.resolve(200)
const p3 = Promise.resolve('11')
Promise.all([p1, p2, p3]).then((res) => {
console.log(res);
})
// [ 202, 200, '11' ]
如果有一个Promise变成了rejected,那么整个Promise是rejected状态。
const p1 = Promise.resolve(202)
const p2 = Promise.reject(400)
const p3 = Promise.resolve('11')
Promise.all([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// 400 err
缺点:对于resolved和依然处于pending状态的Promise,获取不到对应的结果。
Promise.allSettled()在所有的Promise都有结果(settled)后,无论是fulfilled还是reject,才会有最终的状态。并且这个结果一定是fulfilled的。
const p1 = Promise.resolve(202)
const p2 = Promise.reject(400)
const p3 = Promise.resolve('11')
Promise.allSettled([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
/*
[
{ status: 'fulfilled', value: 202 },
{ status: 'rejected', reason: 400 },
{ status: 'fulfilled', value: '11' }
] res
*/
七、类方法-race/any
Promise.race()
竞赛,只要有一个Promise变成fulfilled/rejected状态就结束。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 2000)
})
Promise.race([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// 2 res
如果有一个Promise先变成rejected状态,则整体以rejected状态结束。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 1000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 2000)
})
Promise.race([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// 1 err
Promise.any()等到一个fulfilled状态才会决定新的Promise状态。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(3)
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 1000)
})
Promise.any([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// 2 res
如果所有的Promise都是rejected状态,那也会等到所有的Promise都变成rejected状态才会结束。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(3)
}, 3000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(2)
}, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(1)
}, 1000)
})
Promise.any([p1, p2, p3]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// [AggregateError: All promises were rejected] { [errors]: [ 3, 2, 1 ] } err
八、Promise实践
1.基本使用:封装接口请求
function request (url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === 'foo') {
resolve('200')
} else {
reject('404')
}
}, 2000)
})
}
request('foo1').then(res => {
console.log(res, 'res---');
}).catch((err) => {
console.log(err, 'err---');
})
2.Promise.race():请求超时处理
不同于接口统一超时处理,假设某个请求的超时设置小于统一超时,此时可以使用race处理。
function requestImg () {
return new Promise((resolve, reject) => {
let img = new Image()
img.onload = () => {
// resolve(img)
setTimeout(() => {
resolve(img)
}, 3000)
}
img.src = 'https://es6.ruanyifeng.com/images/cover_thumbnail_3rd.jpg'
})
}
function timeout () {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时')
}, 1000)
})
}
Promise.race([requestImg(), timeout()]).then((res) => {
console.log(res, 'res');
}).catch((err) => {
console.log(err, 'err');
})
// 请求超时 err
3.Promise.all():表单接口请求
所有附件上传成功(接口执行完毕)后,再保存表单数据。
九、面试题
待补充......