Promise是什么

Promise是一个构造函数,它能够接受一个函数作为参数,这个函数包含了两个参数resolve和reject,分别是异步操作执行成功时执行的回调函数和执行失败时执行的回调函数

Promise对象存在三种状态:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)

简单地讲,回调函数resolve(data)做的事情是,将Promise对象标记为执行态,然后进行下一步的操作then((data)=>{//do sth.}),回调函数reject(error)则将Promise对象标记为拒绝态,然后可以catch这个error并执行对应的操作

我们来使用一下Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
function fn1(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步任务1执行完成")
myresolve("返回fn1的数据")
},1000)
})
return p
}
fn1().then((data)=>{
console.log(data)
})

Promise在创建的时候就会直接调用,所以一般的用法是用一个函数包裹Promise,然后将生成的Promise对象返回出来,构造函数中传入的两个参数resolve和reject可以随意命名,不影响使用

Promise最大的优势在于在异步操作执行完后,用链式调用的方式执行回调函数,比如原先需要层层回调的操作,就可以被简化成如下的形式

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
function fn1(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作1执行完成")
myresolve("返回fn1的数据")
},1000)
})
return p
}
function fn2(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作2执行完成")
myresolve("返回fn2的数据")
},1000)
})
return p
}
fn1().then((data)=>{
console.log(data)
return fn2();
}).then((data)=>{
console.log(data)
})

当执行中出现问题的时候,可以用参数reject将对象标记为拒绝态,然后返回原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getpassword(){
return new Promise(function(resolve, reject){
setTimeout(function(){
// 获取输入的密码
var password = "12345"
if(password.length < 6){
reject("密码过短")
} else{
resolve(password)
}
})
})
}
getpassword().then(
function(data){
console.log("密码:"+data);
},
function(data){
console.log(data)
}
)

.then中允许放入两个参数,两个分别用于承接resolve和reject时返回数据的回调函数

Promise的基础实现

基于以上内容,我们可以根据规范来实现一个Promise

实现的核心有几点,一是链式调用,二是状态的维护,当状态改变时才会触发之后的内容

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const PENDING = Symbol()
const FULFILLED = Symbol()
const REJECTED = Symbol()
class MyPromise {
callbacks = [];
state = PENDING;
value = null;
constructor(fn) {
// 绑定this
fn(this._resolve.bind(this), this._reject.bind(this));
}
then(onFulfilled, onRejected) {
// 链式调用,p.then().then()
return new MyPromise((resolve, reject) => {
this._handle({
// onFulfilled和onRejected都可能为空
onFulfilled: onFulfilled || null,
onRejected: onRejected || null,
resolve: resolve,
reject: reject
});
});
}
_handle(callback) {
if (this.state === PENDING) {
// 注册回调函数,用队列是因为可能一个promise不止一个后继节点,可能有p.then(),p.then()的情况
this.callbacks.push(callback);
return;
}

let fn = this.state === FULFILLED ? callback.onFulfilled : callback.onRejected;

if (!fn) { //如果then中没有传递任何东西
fn = this.state === FULFILLED ? callback.resolve : callback.reject;
fn(this.value);
return;
}

let ret = fn(this.value);
fn = this.state === FULFILLED ? callback.resolve : callback.reject;
fn(ret);
}
_resolve(value) {
// 如果是Promise实例,就把当前Promise实例的状态改变接口重新注册到resolve的值对应的Promise的onFulfilled中
if (value && (typeof value === 'object' || typeof value === 'function')) {
var then = value.then;
if (typeof then === 'function') {
then.call(value, this._resolve.bind(this), this._reject.bind(this));
return;
}
}
// 切换状态
this.state = FULFILLED
this.value = value
this.callbacks.forEach(callback => this._handle(callback));
}
_reject(error) {
this.state = REJECTED
this.value = error
this.callbacks.forEach(callback => this._handle(callback));
}
}

Promise.all与实现

Promise.all的作用是并行地执行异步操作,它能接收一个Promise数组,当他们都执行完的时候,将他们resolve的内容拼接成一个数组返回出来

我们先来简单地使用一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function fn1(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作1执行完成")
myresolve("返回fn1的数据")
},1000)
})
return p
}
function fn2(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作2执行完成")
myresolve("返回fn2的数据")
},1000)
})
return p
}
Promise.all([fn1(), fn2()]).then(data => {
console.log(data)
})

我们要实现的只是按promise顺序执行resolve,将返回的data保存在一个数组里返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function MyPromiseAll(promises){
let rets = []
return new Promise((resolve, reject) => {
promises.forEach((promise, id) => {
Promise.resolve(promise).then(data => {
rets[id] = data;
if (rets.length === promises.length) {
resolve(rets);
}
}, reason => reject(reason));
})
})
}
MyPromiseAll([fn1(), fn2()]).then(data => {
console.log(data)
})

Promise.race与实现

Promise.race同样接受一个Promise数组,但是它返回的是最先结束的异步操作得到的数据

比如下方代码,最后只有fn2的数据被返回出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function fn1(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作1执行完成")
myresolve("返回fn1的数据")
},2000)
})
return p
}
function fn2(){
var p = new Promise(function(myresolve, myreject){
// 异步操作
setTimeout(function(){
console.log("异步操作2执行完成")
myresolve("返回fn2的数据")
},1000)
})
return p
}
Promise.race([fn1(), fn2()]).then(data => {
console.log(data)
})

race的实现相比all更简单,直接把所有Promise放进去跑,谁先出来就返回即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyPromiseRace(promises) {
return new Promise(function (resolve, reject) {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(function (value) {
return resolve(value)
}, function (reason) {
return reject(reason)
})
}
})
}
MyPromiseRace([fn1(),fn2()]).then(data => {
console.log(data)
})