■ES6中一个非常重要和好用的特性就是Promise
- Promise是异步编程的一种解决方案
- 使用Promise后异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
■那什么时候我们会来处理异步事件呢?
- 一种很常见的场景应该就是网络请求了
- 我们封装一个网络清求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7-样将结果返回
- 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去
- 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦
- 但是,当网络请求非常复杂时,就会出现回调地狱(看下面的场景)。
■我们来考虑下面的场景(有夸张的成分)
- 我们需要通过一个url1从服务器加载一个数据datal , data1中包含了下一个请求的url2
- 我们需要通过data1取出url2。从服务器加载数据data2,data2中包含了下一个请求的url3
- 我们需要通过data2取出url3。从服务器加载数据data3,data3中包含了下一个请求的url4
- 发送网络请求url4,获取最终的数据data4
■上面的代码有什么问题吗?
- 正常情况下不会有生么问题,可以正常运行并且获得我们想要的结果
- 但是这样的代码不适合维护,我们希望使用一种更加优雅的方法来进行这种异步在操作
- 这种方法就是使用Promise
Promise的基本使用
我们用一个定时器来模拟异步事件
- 在代码中data是从网络上一秒后请求到的数据
- console.log(data)是处理方式
- 先来看看过去的写法
text 代码:setTimeout(() => { let data = "Hello World"; console.log(data); },1000)
换成Promise写法
text 代码: new Promise((resolve,reject) => {
setTimeout(() => {
//请求到的数据
let data = "Hello World";
resolve(data);
reject();
},1000)
}).then(data => {
//请求到数据后要进行的操作
console.log(data);
}).catch(() => {
console.log("error");
})
■Promise是一个对象,需要传入一个回调函数
- 回调函数中有两个参数resolve,reject,它们都是回调函数
- 当请求成功时调用resolve()
- 当请求失败时调用reject()
■调用resolve后会执行then()方法
- then()中需要传入一个回调函数
- 在回调函数中写请求成功后要执行的代码
- then()会返回一个Promise对象
■调用reject后会执行catch()方法
- catch()中需要传入一个回调函数
- 在回调函数中写请求失败后要执行的代码
- catch()会返回一个Promise对象
■可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了
我们在来看看更复杂的情况
- 有一个定时器a,一秒后输出data1
- 在定时器a中,有一个定时器b
- 定时器b,一秒后输出data2
- 在定时器b中,有一个定时器c
- 定时器c,一秒后输出data3
//定时器a
setTimeout(() => {
console.log("data1");
//定时器b
setTimeout(() =>{
console.log("data2");
//定时器c
setTimeout(() =>{
console.log("data3")
},1000)
},1000)
}1000)
(看到这个,有没有想到上面说的回调地狱?)
来看看使用Promise的代码
text 代码: new Promise((resolve,reject) => {
//定时器a
setTimeout(() => {
resolve();
},1000)
}).then(() => {
console.log("data1");
return new Promise((resolve,reject) => {
//定时器b
setTimeout(() => {
resolve();
},1000)
}).then(() => {
console.log("data2");
return new Promise((resolve,reject) => {
//定时器c
setTimeout(() => {
resolve();
},1000)
}).then(() => {
console.log("data3");
}).catch(() => {
console.log("error");
})
}).catch(() => {
console.log("error");
})
}).catch(() => {
console.log("error");
})
- 在使用Promise时,如果在请求a中还有一个b请求,那我们可以在a的then中返回一个Promise对象,将b请求放在这里面执行。
- 看到这个代码是不是突然觉得我还是用以前的方法好!(套娃警告)
不急,我们来看看清楚一点的写法
text 代码: //第三个请求
function req3(){
let p3 = new Promise((resolve,reject) => {
//定时器3
setTimeout(() => {
resolve()
},1000)
}).then(() => {
console.log("data3")
}).catch(() => {
console.log("error");
})
return p3;
}
//第二个请求
function req2(){
let p2 = new Promise((resolve,reject) => {
//定时器2
setTimeout(() => {
resolve()
},1000)
}).then(() => {
console.log("data2")
//执行第三个请求
req3();
}).catch(() => {
console.log("error");
})
return p2;
}
//第一个请求
new Promise((resolve,reject) => {
//定时器1
setTimeout(() => {
resolve()
},1000)
}).then(() => {
console.log("data1");
//执行第二个请求
req2();
}).catch(() => {
console.log("error");
})
是不是比上一个清楚多了,虽然看起来比以前的写法多了好多代码,但却完美的解决了地狱回调的问题,并且把每个请求的执行代码和处理结果的代码清晰地分离了。
Promise的三种状态
■当我们开发中有异步操作时,就可以给异步操作包装一个Promise
- 异步操作后会有三种状态
■Promise的三种状态
- pending:等待状态,比如正在进行网络请求,或者定时器没有到时间
- fulfill:满足状态,当我们主动回调resolve时,就处于该状态,并且会回调then()
- reject :拒绝状态,当我们主动回调reject 时,就处于该状态,并且会回调catch()