ES6中一个非常重要和好用的特性就是Promise,
1.那么Promise是做什么的呢?
Promise是异步编程的一种解决方案
。
2.一般什么时候会处理异步事件呢?
一种很常见的就是网络请求
,一般网络请求都是需要时间等待返回数据的,但是我们又不想我们程序阻塞在那里,我们就需要使用异步编程,开启一个异步任务去执行网络请求,异步任务执行完毕后再进行函数的回调来处理数据。
一般定时器,这么使用
// 使用 setTimeout,1s后执行打印 Hello World
setTimeout(() => {
console.log("Hello World")
}, 1000);
封装定时任务,Promise是一个对象,使用时直接new
new Promise();
那么看下这个对象的函数(java中叫做有参构造函数)中,需要用到什么参数呢?
Promise(executor: (resolve: (value: any) => void, reject: (reason?: any) => void) => void): Promise<any>
需要一个executor
函数,executor函数中有有两个函数,resolve
(解决)和reject
(拒绝)
new Promise((resolve, reject) => {
// 1s后执行setTimeout中的函数回调,然后调用 resolve()又去回调了 then中的函数执行具体的操作
setTimeout(() => {
resolve() // 执行resolve函数,再找then()执行
}, 1000);
}).then(() => {
console.log("Hello World");
})
上面是一个普通的定时任务,假如是网络请求后拿到了数据,我们改在哪里处理,怎么拿到该处理的数据呢?
new Promise((resolve, reject) =>{
// 假如这里执行了网络请求
处理网网络请求函数{
function(res){
resolve(res.data); // 成功时传入 数据,再走 then
},
function(err){
reject("err message"); // 失败时,传入错误信息
}
);
}).then(data => {
// 处理data数据,与页面进行交互
}).catch(err => {
// 捕获异常,进行处理
})
当我们开发中有异步操作时,可以给异步操作包装一个Promise。
了解一下:
sync : 同步操作
async : 异步操作
异步操作之后会有三种状态:
其实除了上面说的 resolve回调then(),reject回调catch外,then中也可以同时处理resolve和reject。看一下then中需要的函数:
then(onfulfilled?: (value: any) => any, onrejected?: (reason: any) => PromiseLike<never>): Promise<any>
then中可接收两个函数,一个是处理fulfilled的(调用了resolve()的满足状态),另一个就是处理rejected的(调用了reject()的拒绝状态)。
具体实例:
new Promise((resolve, reject) =>{
// 假如这里执行了网络请求
axios.get("http://127.0.0.1:8080/app/report/test").then(
function(res){
resolve(res.data);
},
function(err){
reject("err message");
}
);
}).then(data => { // 成功时调用第一个函数
// 处理数据 data
}, err => { // 失败时,调用第二个函数
// 处理错误信息
console.log(err)
})
我们在使用Promise时无论是调用完then还是catch最后都会返回一个Promise对象。那么我们的代码其实是可以链式调用的
假如要完成一个情景,我们进行了网络请求拿到了数据 abc,然后需要给abc拼接一些东西,而且要拼接好几次,那么如何处理呢?
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('abc')
}, 1000);
}).then(res => { // 每次then执行完 返回一个 Promise对象
console.log("第一层封装")
return new Promise((resolve, reject) => {
resolve(res + "1111")
})
}).then(res => {
console.log("第二层封装")
return new Promise(resolve => { // new Promise,若只需要resolve函数,那么reject可省略
resolve(res + "2222")
})
}).then(res => {
console.log(res, "返回结果")
})
上文中
return new Promise((resolve, reject) => {
resolve(res + "1111")
})
可以简写为
return Promise.resolve(res + "1111")
具体实现:
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('abc')
}, 1000);
}).then(res => {
console.log("第一层封装")
return Promise.resolve(res + "1111")
}).then(res => {
console.log("第二层封装")
return Promise.resolve(res + "2222")
}).then(res => {
console.log(res, "返回结果")
})
then中也会自动返回Promise的封装,也就是此句仍可简写:
return Promise.resolve(res + "1111")
简写为:
return res + "1111"
具体实现:
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('abc')
}, 1000);
}).then(res => {
console.log("第一层封装")
return res + "1111"
}).then(res => {
console.log("第二层封装")
return res + "2222"
}).then(res => {
console.log(res, "返回结果")
})
假如中间执行时,出现了故障
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('abc')
}, 1000);
}).then(res => {
console.log("第一层封装")
return Promise.reject("失败了") // 这里出现故障,执行了reject,会去直接找catch,其他跳过
}).then(res => {
console.log("第二层封装")
return res + "2222"
}).then(res => {
console.log(res, "返回结果")
}).catch(err => {
console.log(err)
})
其实不止执行Promise.reject()走到catch中,我们手动抛出的异常,catch仍然可以捕获到。
// 方式一
return Promise.reject("失败了")
// 方式二
throw "失败了"
假如有一个场景:需要两个网络请求都获取到数据后,再进行后续的数据处理。通常情况下我们是怎么处理的呢?我们可能会这么做:在每个回调方法内部都去执行一个函数,在这个函数中判断两个请求的数据是否都获取到了,然后再进行处理。
let result1 = false
let result2 = false
ajax({
url:"",
success: function(){
...
result1 = true
handlerResult()
}
})
ajax({
url:"",
success: function(){
...
result2 = true
handlerResult()
}
})
function handlerResult(){
if(result1 && result2) {
// 处理数据
}
}
但是呢,过于麻烦。Promise为我们提供了这种场景的解决方案。
下面以setTimeout模拟网络请求延迟后,获取到数据
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name:"wlh1", age:21})
}, 1000);
}),
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name:"wlh2", age:21})
}, 2000);
})
]).then(results => { // 只有当两个Promise包装的异步请求执行完以后,才会执行then中的
console.log(results[0]) // 第一个网络请求的结果
console.log(results[1]) // 第二个网络请求的结果
})