谈到JavaScript相信它的事件回调大家肯定都听过。即使只有一个执行线程,它也能处理异步高并发。
同样很多人回调写多了都不可避免的会变成Callback Hell(回调地狱),这样的代码会非常复杂难懂,因为回调不像同步代码,它的执行顺序不是从上至下的,读回调代码必须反复横跳,思考什么情况下回跳到什么地方。
Promise就是一种异步编程,很早就有了随着ES6的发布Promise也成了JavaScript原生支持的对象,但学校这学期的课程并没有相关章节。因此顺便学习下Promise对象的使用。

以JavaScript原生的AJAX为栗子,AJAX的请求形式如下:

1
2
3
4
5
6
7
8
9
10
11
12
function ajax(url, callback) {
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.send()
xhr.onload = function() {
if (this.status == 200) {
callback(JSON.parse(this.response))
} else {
throw new Error("加载失败")
}
}
}

我们就以此方式依次请求三个接口(地址),假设响应体的JSON格式是{id: 1, name: "farmer"}, JS代码如下:

1
2
3
4
5
6
7
8
ajax('http://127.0.0.1:5003/?name=farmer', (user) => {
// 先拿到一个东西,再用这个东西去查别的,比如通过用户名查该用户的资料
ajax('http://127.0.0.1:5003/?name=' + user.name, (e) => {
ajax('http://127.0.0.1:5003/?name=' + user.name, (e) => {
console.log(e)
})
})
})

是不是觉得嵌套很多? 如果连续请求更多的地址,那嵌套就要看傻了。 接下来我们试试用Promise对象对ajax进行封装,看看效果怎么样。 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.send()
xhr.onload = function () {
if (this.status == 200) {
resolve(JSON.parse(this.response))
} else {
reject("加载失败")
}
}
xhr.onerror = function () {
reject(this)
}
})
}

上例中,我们自己封装的AJAX会返回一个“Promise”对象,初始化该对象时我们要传入一个函数,这个函数又接受两个回调函数作为参数,分别是resolverejectresolve就是你操作成功时调用的方法,reject就是失败时调用的。接下来让我们同样实现前面三个接口的功能:

1
2
3
4
5
6
7
8
9
10
11
12
let url = "http://127.0.0.1:5003/"
ajax(`${url}?name=xiaotao`).then(
value =>{
// 这里可以通过第一个数据再发送请求
return ajax(`${url}?name=` + value.name)
}
).then(value =>{
return ajax(`${url}?name=` + value.name)
}).then(
value => console.log(value),
reason => console.log(reason)
)

大家是不是发现,本来长长的嵌套,变成了顺序执行的代码了? 到此就是Promise的简单探讨了。 如果你对上面的栗子还不太清楚,那下面是一些Promise对象更基础、详细的解释or栗子。希望能帮到你~😃

Promise怎么用?

相信刚接触Promise的童鞋都比较迷惑,到底应该怎么用Promise。其实上面有提到过,初始化该对象时我们要传入一个函数(我这里使用的是匿名函数),这个函数又接受两个回调函数作为参数(这里读起来比较拗口,可以和下面的栗子结合起来看),分别是resolverejectresolve就是你操作成功时调用的方法,reject就是失败时调用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// resolved 成功
// rejected 拒绝
new Promise((resolve, reject) => {
// 工作流程
resolve("成功状态")
// reject("失败状态")
}).then( // then -> 微任务列表
// 工作后的处理
// then方法有两个参数,第一个是成功的回调函数,第二个是失败的
value => {
// 成功
console.log("成功业务处理-1")
},
reason => {
// 失败
console.log('失败(拒绝)的业务处理-1')
}
).then(
value => console.log("成功业务处理-2"),
reason => console.log('失败(拒绝)的业务处理-2')
// 上面一个Then可能不太清晰,这里和上面的逻辑是一样的。
// then有两个参数,这两个参数是两个回调函数。
// 一个是成功状态的回调函数,对应这里的value。 而这个成功状态则是上面的resolve
// 一个是失败状态的回调函数,对应这里的reason。而这个失败状态则是上面的reject
)

Then的更多用法

我们从上面的栗子可以看到,then函数也有两个参数,也是一个成功一个失败。其实then也是一个Promise。
但从上面的栗子也可以发现,第二个then无论前一个then是哪个参数处理的,都只会出发成功状态。如果我们想让第二个then的失败处理也能生效,我们应该将代码改成下面这样:

1
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
28
let p1 = new Promise((resolve, reject) => {
resolve('fulfilled')
})

let p2 = p1.then(
value => {
console.log(value)
// 第一个Then return的内容, Then默认返回成功(resolve)
// return 'Farmer'
// 返回一个Promise
return new Promise((resolve, reject) => {
// resolve("解决")
reject("Then 解决失败")
})
},
reason => {
console.log(reason)
return new Promise((resolve, reject) => {
// resolve("解决")
reject("Then 解决失败")
})
}
).then(
value => {
console.log(value)
},
reason => console.log(reason)
)

当第一个then返回一个Promise对象后,第二个then就能根据前一个then返回的Promise的状态进行处理。

总结

关于Promise一些更深层次的理论这里就不探讨了,网上资料很多,大家可以去搜索下。本文中的示例代码可以在这里下载