Promies
Promies
什么是 Promies
简单说 Promise 是异步编程的一种解决方案。
Promise 是 ES6 中的特性。
什么是异步操作?
网络请求中,对端服务器处理需要时间,信息传递过程需要时间,不像我们本地调用一个 js 加法函数一样,直接获得1+1=2
的结果。这里网络请求不是同步的有时延,不能立即得到结果。
如何处理异步事件?
对于网络请求这种,一般会使用回调函数,在服务端传给我数据成功后,调用回调函数。例如 ajax 调用。
$.ajax({
success:function(){
...
}
})
2
3
4
5
如果碰到嵌套网络请求,例如第一次网络请求成功后回调函数再次发送网络请求,这种代码就会让人很难受。
$.ajax({
success:function(){
$.ajax({
...
})
}
})
2
3
4
5
6
7
如果还需要再次网络请求,那么又要嵌套一层,这样的代码层次不分明很难读,也容易出问题。
Promise 的基本使用
什么时候使用 Promise
解决异步请求冗余这样的问题,promise 就是用于封装异步请求的。
Promise 对象
new Promise((resolve, reject) => {})
Promise 对象的参数是一个函数(resolve, reject) => {}
,这个函数又有 2 个参数分别是resolve
和reject
。这 2 个参数本身也是函数,是不是有点绕?后面还有回调函数then(func)
的参数也是一个函数。
模拟定时器的异步事件
用定时器模拟网络请求,定时一秒为网络请求事件,用 console.log()表示需要执行的代码。
//1.使用setTimeout模拟嵌套的三次网络请求
setTimeout(() => {
//第一次请求
console.log('hello world') //第一次处理代码
setTimeout(() => {
//第二次请求
console.log('hello vuejs') //第二次处理代码
setTimeout(() => {
//第三次请求
console.log('hello java') //第三次处理代码
}, 1000)
}, 1000)
}, 1000)
2
3
4
5
6
7
8
9
10
11
12
13
一层套一层,看起是不是很绕。
使用 promise 来处理异步操作
//参数 -> 函数
// resolve和reject本身也是函数
//then()的参数也是一个函数
new Promise((resolve, reject) => {
setTimeout(() => {
//第一次网络请求
resolve()
}, 1000)
}).then(() => {
console.log('hello world') //第一次处理代码
return new Promise((resolve, reject) => {
setTimeout(() => {
//第二次网络请求
resolve()
}, 1000).then(() => {
console.log('hello vuejs') //第二次处理代码
return new Promise((resolve, reject) => {
setTimeout(() => {
//第三次网络请求
resolve()
}, 1000)
}).then(() => {
console.log('hello java') //第三次处理代码
})
})
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
是不是觉得代码还要更复杂了?仔细看看第一个如果使用了多个就找不到对应关系了。相反第二个流程就很清楚,调用resolve()
就能跳转到then()
方法就能执行处理代码,then()
回调的返回值又是一个Promise
对象。层次很明显,只要是then()
必然就是执行处理代码,如果还有嵌套必然就是返回一个 Promise 对象,这样调用就像 java 中的 StringBuffer 的 append()方法一样,链式调用。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 1000).then(success => {
console.log(success)
})
})
2
3
4
5
6
7
setTimeout()模拟的是网络请求,而 then()执行的是网络请求后的代码,这就将网络请求和请求得到响应后的操作分离了,每个地方干自己的事情。在 resolve 中传参了,那么在 then()方法中的参数就有这个参数,例如 data。
网络请求中也会有失败情况?例如网络堵塞。
如何处理失败情况,此时就要用到 reject()
new Promise((resolve, reject) => {
setTimeout(() => {
reject('error message')
}, 1000).catch(error => {
console.log(error)
})
})
2
3
4
5
6
7
此时reject(error)
,catch()
方法捕获到reject()
中的 error。
合起来
new Promise((resolve, reject) => {
setTimeout(() => {
// 成功的时候调用resolve()
// resolve('hello world')
// 失败的时候调用reject()
reject('error message')
}, 1000)
.then(success => {
console.log(success)
})
.catch(error => {
console.log(error)
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
拿 ajax 来举例子:
new Promise((resolve, reject) => {
$.ajax({
success: function () {
// 成功的时候调用resolve()
// resolve('hello world')
// 失败的时候调用reject()
reject('error message')
},
})
.then(success => {
console.log(success)
})
.catch(error => {
console.log(error)
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Promise 的三种状态
- pending:等待状态,比如正在进行的网络请求还未响应,或者定时器还没有到时间
- fulfill:满足状态,当我们主动回调了 resolve 函数,就处于满足状态,并会回调 then()
- reject:拒绝状态,当我们主动回调 reject 函数,就处于该状态,并且会回调 catch()
Promies 的链式调用
- 网络请求响应结果为 hello ,打印 hello
- 处理: hello world ,打印 hello world
- 处理: hello world,vuejs ,打印 hello world,vuejs
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
}).then(res => {
console.log(res) //打印hello
return new Promise(resolve => {
resolve(res + ' world')
}).then(res => {
console.log(res) //打印hello world
return new Promise(resolve => {
resolve(res + ',vuejs')
}).then(res => {
console.log(res) //打印hello world,vuejs
})
})
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
链式调用就是then()
方法的返回值返回一个 Promise 对象继续调用then()
,此外还有简写Promise.resolve()
。
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
})
.then(res => {
console.log(res) //打印hello
return Promise.resolve(res + ' world')
})
.then(res => {
console.log(res) //打印hello world
return Promise.resolve(res + ',vuejs')
})
.then(res => {
console.log(res) //打印hello world,vuejs
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
还可以直接省略掉Promise.resolve()
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
})
.then(res => {
console.log(res) //打印hello
return res + ' world'
})
.then(res => {
console.log(res) //打印hello world
return res + ',vuejs'
})
.then(res => {
console.log(res) //打印hello world,vuejs
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果中途发生异常,可以通过catch()
捕获异常
new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hello')
}, 1000)
})
.then(res => {
console.log(res) //打印hello
return res + ' world'
})
.then(res => {
console.log(res)
// return Promise.reject('error message')//发生异常
throw 'error message' //抛出异常
})
.then(res => {
console.log(res) //打印hello world,vuejs
})
.catch(error => {
console.log(error)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
也可以通过throw
抛出异常,类似 java
throw 'error message' //抛出异常
Promies 的 all 使用
有这样一个情况,一个业务需要请求 2 个地方(A 和 B)的数据,只有 A 和 B 的数据都拿到才能走下一步。
ajax 实现
$.ajax({
...//结果A
resultA = true
callback()
})
$.ajax({
...//结果B
resultB = true
callback()
})
//回调函数
function callback(){
if(resultA&&resultB){
...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
由于不知道网络请求 A 和网络请求 B 哪个先返回结果,所以需要定义一个函数只有 2 个请求都返回数据才回调成功。
Promise 实现
Promise.all([
new Promise((resolve, resjct) => {
$.ajax({
url: 'url1',
success: function (data) {
resolve(data)
},
})
}),
new Promise((resolve, resjct) => {
$.ajax({
url: 'url2',
success: function (data) {
resolve(data)
},
})
}).then(results => {
console.log(results)
}),
])
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
上面是伪代码,只是包装了 ajax,ajaxA 和 ajaxB 的结果都放在resolve()
中,Promise 将其放在results
中了,使用setTimeout
模拟。
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => {
// 请求A
resolve('结果A')
}, 1000)
}),
new Promise((resolve, reject) => {
setTimeout(() => {
// 请求B
resolve('结果B')
}, 1000)
}),
]).then(results => {
console.log(results)
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16