背景说明
从同步与异步说起:
JavaScript 中具有同步和异步的概念,最常⻅的异步操作莫过 Ajax,Ajax 是前后端数据交互的桥
梁。
异步操作特点:
同步任务总是顺序执⾏的。与同步任务不同,异步任务执⾏结果与书写顺序⽆关,例如,定时器结束时
间取决于延迟时间;Ajax 的响应成功时间取决于数据量与⽹速。
这就导致,如果要对异步操作的结果进⾏处理,就必须使⽤回调函数(
callback)。
问题出现:
思考:如果有多个请求,每个请求都依赖与前⼀个请求的结果,这时会发⽣什么呢?
这种层层嵌套的回调写法称为“回调地狱”
Promise 的作⽤:
Promise 是 ES2015(
ES6)中标准化的功能,⽤于 给异步编程提供⼀种书写更合理,功能更强的统
⼀的解决⽅案。
Promise 含义为承诺,是⼀个 ES6 提供的类型,使⽤时需要创建⼀个实例对象,指代我们要进⾏的⼀个操作(异步)
创建完毕, 这时 promise 处于⼀个“待定状态”, 最终结果可能为“成功状态”或“失败状态”。
举例增强理解:明天发⼯资,我要给我媳妇买个苹果,如果发⼯资了,就买⼀⽄,如果没发就算了,因为没有私房钱。
特点:结果明确后⽆法更改:例如,发⼯资后买了⼀⽄苹果,买了就是买了,不可能过⼀会⼉⼜变成了没买,这不合逻辑。
回归操作:那么对应操作中,利于⼀个 Ajax 请求,当请求成功或失败时,可以通过相应的“状态”进⾏
执⾏结果的判断,从⽽进⾏相应的处理。
⼩结:Promise 对象的操作就是通过状态进⾏控制,状态变化会⾃动触发对应的回调。
Promise 使⽤
创建实例⽤于存储异步操作
参数为函数,在创建实例时同步执⾏
函数有两个参数,都是函数,调⽤时可决定 promise 对象的状态
resolve(data) 传递成功时的结果
reject(err) 传递失败时的错误信息
注意,由于promise 对象的结果明确后⽆法更改,所以两个函数在逻辑中只能有⼀个被执⾏。
const
promise
= new
Promise(
function
() {
resolve(100)
// reject(new Error('error message!'))
})
当 promise 对象的状态确定时,会触发对应的回调函数进⾏处理,我们需要通过 promise 对象的⽅法指定这些回调函数:
then() 成功时的处理函数
catch() 失败时的处理函数
1
const
promise
= new
Promise(
function
(
resolve
,
reject
) {
2
resolve
(
100
)
3
// reject(new Error('error message!'))
4
})
5
//
也可以通过
new Promise().then().catch()
⽅式进⾏链式调⽤。
6
promise.
then
(
function
(
value
) {
7
console.
log
(
'
成功了,数据为:
'
,
value
)
8
}).
catch
(
function
(
error
) {
9
console.
log
(
'
失败了,错误为:
'
,
error
)
10
})
Promise 练习:封装 Ajax 函数
1
function
ajax
(
url
) {
2
return new
Promise(
function
(
resolve
,
reject
) {
3
const
xhr
= new
XMLHttpRequest()
4
xhr
.
open
(
'GET'
,
url
)
5
xhr
.
responseType
=
'json'
6
xhr
.
onload
= function
() {
7
if
(
this
.
status
===
200
) {
8
//
接收到响应数据,标记状态为成功,并传递给
then
中的回调处理
9
resolve
(
this
.
response
)
10
}
else
{
11
//
失败时更改状态,并传递错误信息
12
reject
(
new
Error(
this
.
statusText
))
13
}
14
}
15
//
发送请求
16
xhr
.
send
()
17
})
18
}
19
const
successUrl
=
'http://edufront.lagou.com/front/course/getPur
chaseCourse'
20
const
failUrl
=
'http://eduboss.lagou.com/boss/course/changeStat
e'
21
22
ajax(failUrl).
then
(
function
(
res
) {
23
console.
log
(
res
)
24
}).
catch
(
function
(
error
) {
25
console.
log
(
error
)
26
})
如果仅仅是这样使⽤,请求多了,依然会出现嵌套的问题,这时要注意使⽤⽅式,不要嵌套书写,⽽是利
⽤ Promise 的链式调⽤进⾏操作。
1
//
错误写法
2
ajax(successUrl)
3
.
then
(
function
(
res
) {
4
console.
log
(
res
)
5
ajax(successUrl)
6
.
then
(
function
(
res
) {
7
console.
log
(
res
)
8
ajax(successUrl)
9
.
then
(
function
(
res
) {
10
console.
log
(
res
)
11
})
12
})
13
})
链式调⽤
then() 调⽤的返回值为⼀个新的 Promise 对象,如果要在⼀次异步操作后再执⾏下⼀个异步操作,应当通过这个新的 Promise 对象进⾏处理,这样就可以避免回调地狱啦。
1
//
执⾏示例
2
ajax(successUrl)
3
.
then
(
function
(
res
) {
4
console.
log
(
1
)
5
})
6
.
then
(
function
(
res
) {
7
console.
log
(
2
)
8
})
9
.
then
(
function
(
res
) {
10
console.
log
(
3
)
11
})
在 then() 中通过 return 返回 promise 对象会被设置为 then() 的返回值。
1
//
顺序执⾏异步操作实例
2
ajax(successUrl)
3
.
then
(
function
(
res
) {
4
console.
log
(
1
,
res
)
5
return
ajax(successUrl)
6
})
7
.
then
(
function
(
res
) {
8
console.
log
(
2
,
res
)
9
return
ajax(successUrl)
10
})
11
.
then
(
function
(
res
) {
12
console.
log
(
3
,
res
)
13
return
ajax(successUrl)
14
})
如果返回的是普通值,则会包裹在默认的 promise 对象中,传递给 then ⽅法,不传时默认为 undefined
1
ajax(successUrl)
2
.
then
(
function
(
res
) {
3
console.
log
(
1
,
res
)
4
return
ajax(successUrl)
5
})
6
.
then
(
function
(
res
) {
7
console.
log
(
2
,
res
)
8
// return ajax(successUrl)
9
return
[
1
,
2
,
3
]
10
})
11
.
then
(
function
(
res
) {
12
console.
log
(
3
,
res
)
// 3, [1, 2, 3]
13
return
ajax(successUrl)
14
})
总结:promise 本质上就是通过回调函数定义异步任务结束后所需要执⾏的任务,只不过回调是通过
then() 进⾏设置,由于 then() 可以进⾏链式调⽤,所以避免了层层嵌套的书写⽅式。