Promise 的基本使用

🔷Promise到底是做什么的呢?

  🔹Promise是异步编程的一种解决方案。


🔷那什么时候我们会来处理异步事件呢?

  🔹一种很常见的场景应该就是网络请求了。

  🔹 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。

  🔹所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。

  🔹如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。

但是,当网络请求非常复杂时,就会出现回调地狱。

OK,我以一个非常夸张的案例来说明。

🔷我们来考虑下面的场景(有夸张的成分):

  🔹我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2

  🔹我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3

  🔹我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4

  🔹发送网络请求url4,获取最终的数据data4

🔷上面的代码有什么问题吗?

  🔹正常情况下,不会有什么问题,可以正常运行并且获取我们想要的结果。

  🔹但是,这样额代码难看而且不容易维护。

  🔹我们更加期望的是一种更加优雅的方式来进行这种异步操作。

🔷如何做呢?就是使用Promise。

  🔹Promise可以以一种非常优雅的方式来解决这个问题。

我们先来看看Promise最基本的语法。

这里,我们用一个定时器来模拟异步事件:

假设下面的data是从网络上1秒后请求的数据

console.log就是我们的处理方式。

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
29
30
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
}, 1000)
}).then(function() {
console.log("你好!!!");
console.log("你好!!!");
console.log("你好!!!");
console.log("你好!!!");
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
}, 1000)
})
}).then(function() {
console.log("hello");
console.log("hello");
console.log("hello");
console.log("hello");
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve()
}, 1000)
})
}).then(function() {
console.log("hello");
console.log("hello");
console.log("hello");
console.log("hello");
})

一般情况下是有异步操作时,使用Promise对这个异步操作进行封装,

new-> 构造函数(1.保存一些状态信息,2.执行传入的函数)

在执行传入的回调函数的时候,会传入两个参数,resolve,reject,他们本身又是函数,

当请求操作成功的时候,会调用resolve() 函数,拿到请求到的数据会在new Promise 出来的Promise对象的 then 方法中进行操作,then方法的参数又是一个函数。

当请求操作失败的时候,会调用reject() 函数,得到失败的信息,将会被new Promise 出来的Promise对象的 catch 方法捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("hello Promise")
// reject("error message!!!")
}, 1000)
}).then(function(data) {
console.log(data);
console.log(data);
console.log(data);
console.log(data);

}).catch(function(error) {
console.log(error);
})

Promise三种状态

🔹pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。

🔹fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()

🔹reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()



Promise链式调用

无论是then还是catch都可以返回一个Promise对象。所以,我们的代码其实是可以进行链式调用的:

这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了

Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数

Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数


链式调用简写

简化版代码:

如果我们希望数据直接包装成Promise.resolve,那么在then中可以直接返回数据

注意下面的代码中,我讲return Promise.resovle(data)改成了return data。结果依然是一样的


Promise.prototype.then()

Promise实例具有then方法,也就是说then方法时定义在原型对象上的。

它的作用是为Promise实例添加状态改变时的回调函数。

前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数(可选)。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)因此可以采用链式写法,即then方法后面再调用另一个then方法。

采用链式的then可以指定一组按照次序调用的回调函数。这时,前一个回调函数可能返回一个还是Promise对象(即有异步操作),这时候一个回调函数就会等该Promise对象的状态发生变化,才会被调用。


Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

1
2
3
4
5
6
7
8
9
10
11
new Promise(function(resolve, reject) {
setTimeout(function() {
// resolve("hello Promise")
reject("error message!!!")
}, 1000)
}).then(function(data) {
console.log(data);

}).catch(function(error) {
console.log(error); // 打印错误信息
})

Promise.all()

Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。

当我们又这样的一个需求的时候:我们需要发两次请求或者多次请求,我们需要等这些请求都完成之后,再进行一个需求操作。那么这个时候我们如何来实现的?我们就可以使用 Promise.all() 。

我们这里还是使用 setTimeout() 来模拟请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Promise.all([
new Promise(function(resolve, reject) { // 网络请求一
setTimeout(function() {
resolve({ //网络请求一得到的数据
name: "阿清",
age: 18
})
}, 3000)
}),

new Promise(function(resolve, reject) { //网络请求二
setTimeout(function() {
resolve({ //网络请求二得到的数据
name: "大大怪将军",
age: 19
})
}, 1000)
})
]).then(function(results) {
console.log(results);
})

当网络请求都完成的时候,就会执行 Promise.all() 的then方法。网络请求的数据都会放在 results 数组中。然后我们就可以对其进行操作。

需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即请求1的结果在前,即便请求1的结果获取的比请求2要晚。


Promise.race的使用

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Promise.race([
new Promise(function(resolve, reject) { // 网络请求一
setTimeout(function() {
resolve({
name: "阿清",
age: 18
})
}, 3000)
}),

new Promise(function(resolve, reject) { //网络请求二
setTimeout(function() {
resolve({
name: "大大怪将军",
age: 19
})
}, 1000)
})
]).then(function(results) {
console.log(results);
})


es6 Promise教程 阮一峰