Promise

# Promise

[TOC]

# 一、Promise的使用

摘自知乎


// 订外卖就是一个Promise,Promise就是承诺的意思
// 和商家达成的承诺,无论是做好还是烧糊,都会给予答复。
function orderTakeout() {
    // Promise 接收两个参数
    // resolve 异步事件成功时调用
    // reject 异步事件失败时调用
    return new Promise((resolve, reject)=>{
        let result = cook();
        // 商家的反馈
        if(result==='ok') {
            resolve('sending');
        }else {
            reject('sorry');
        }
    });
}

// 商家厨房做饭,模拟概率事件
function cook() {
    return Math.random()>0.5 ? 'ok' : 'sorry';
}

orderTakeout().then(res=>console.log(res))
.catch(res=>console.log(res));
// 等价于
// orderTakeout().then(res=>console.log(res), res=>console.log(res));
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

# 二、Promise流程图

promise

# 2.1 Promise/A+规范

  • 三种状态 pending|fulfilled(resolved)|rejected

  • 当处于 pending状态的时候,可以转移到 fulfilled(resolved)或者 rejected状态

  • 当处于 fulfilled(resolved)状态或者 rejected状态的时候,就不可变。

  • 必须有一个 then异步执行方法, then接受两个参数且必须返回一个promise。

// onFulfilled 用来接收promise成功的值
// onRejected 用来接收promise失败的原因
promise1=promise.then(onFulfilled, onRejected);
1
2
3

# 三、手写myPromise()

参考链接:https://juejin.im/post/5c6ad98e6fb9a049d51a0f5e (opens new window)

# 3.1 无异步,无链式调用

// 版本一
function myPromise(executor) {
    let self = this;
    self.status = "pending" //定义状态改变前的初始状态
    self.value = undefined; //定义状态为resolved的时候的状态
    self.reason = undefined; //定义状态为rejected的时候的状态
    function resolve(value) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
        }
    }

    function reject(reason) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
        }
    }
    //捕获构造异常
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

myPromise.prototype.then = function (onFullfilled, onRejected) {
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
}
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
// 测试

let p = new myPromise(function (resolve, reject) {
    console.log('start')
    resolve('data')
})
p.then(
    (v) => {
        console.log('success ' + v)
    },
    (v) => {
        console.log('error ' + v)
    }
)
console.log('end')

// start
// success data
// end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3. 2 添加发布订阅模式

let p = new Promise(function (resolve, reject) {
  console.log('start');
  setTimeout(function(){
      resolve('data')
  },2000);
});
p.then(
  (v) => {
    console.log('success: ' + v)
  },
  (v) => {
    console.log('error: ' + v)
  }
);
p.then(
  (v) => {
    console.log('success: ' + v)
  },
  (v) => {
    console.log('error: ' + v)
  }
);
console.log('end');

// start
// end
// 两秒后
// success: data
// success: data
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
  • 对于异步情况,当代码执行到p.then()时,resolve()会随着setTimeout放到异步任务队列中。

  • 在此期间实例p的状态还是默认状态,p.then()不知道该执行哪个参数,那岂不是要干等setTimeout去执行?实际上,在不知道哪个回调会被执行的情况下,我们可以先把两个都执行了,并把它们保存起来(订阅),等要用的时候发布一下就好了。

function myPromise(executor){
  ...
  // 使用数组是因为then方法可以调用多次
  // 用来保存then 方法中,第一个参数
  self.onResolvedCb = []
  // 用来保存then 方法中,第二个参数
  self.onRejectedCb = []
  ...
  
  function resolve(value) {
      ...
      self.onResolvedCb.forEach(fn => fn());
  }
  function reject(reason) {
      ...
      self.onRejectedCb.forEach(fn => fn());
  }
}
myPromise.prototype.then = function(onFulfilled, onRejected){
  ...
  if(self.status === 'pending'){
  // 订阅
    self.onResolvedCb.push(function(){
      onFulfilled(self.value)
    })
    self.onRejectedCb.push(function(){
      onRejected(self.reason)
    })
  }
  ...
}
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
// 版本二
function myPromise(executor) {
    let self = this;
    self.status = "pending" //定义状态改变前的初始状态
    self.value = undefined; //定义状态为resolved的时候的状态
    self.reason = undefined; //定义状态为rejected的时候的状态
    
    // 用来保存then 方法中,第一个参数
    self.onResolvedCallbacks = [];
    // 用来保存then 方法中,第二个参数
    self.onRejectedCallbacks = [];
    
    function resolve(value) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
            self.onResolvedCb.forEach(fn => fn());
        }
    }

    function reject(reason) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
            self.onRejectedCb.forEach(fn => fn());
        }
    }
    //捕获构造异常
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    if(self.status === 'resolved'){
        onFulfilled(self.value);
    }
    if(self.status === 'rejected'){
        onRejected(self.reason);
    }
    if(self.status === 'pending'){
        // 订阅
        self.onResolvedCb.push(function(){
            onFulfilled(self.value);
        })
        self.onRejectedCb.push(function(){
            onRejected(self.reason);
        })
    }
}
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

# 3.3 添加链式调用

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

myPromise.prototype.then = function(onFulfilled, onRejected){
  let self = this
  return new myPromise(function(resolve, reject){
	if(self.status === 'resolved'){...}
	if(self.status === 'rejected'){...}
	if(self.status === 'pending'){...}
  }
} 
1
2
3
4
5
6
7
8

光返回新实例是不够的,还应该把本轮的参数传递给下一个then方法的回调函数中去。

# 3.3.1 对于then中函数返回值的处理 非Promise情况

myPromise.prototype.then = function(onFulfilled, onRejected){
  ...
  if(self.status === 'resolved'){
    try{
      let x = onFulfilled(self.value)
      resolve(x)
    }catch(e){
      reject(e)
    }
  }
  ...
}
1
2
3
4
5
6
7
8
9
10
11
12
myPromise.prototype.then = function(onFulfilled, onRejected){
    let self = this
    let promise2 = new myPromise(function(resolve, reject){
        // then 函数的成功回调函数的执行结果 与 promise2的关系
        if(self.status === 'resolved'){
            try{
                let x = onFulfilled(self.value)
                resolve(x) // 这是 x 是常量的时候,但x可能是一个新的promise,
            }catch(e){
                reject(e)
            }
        }
        if(self.status === 'rejected'){
            try{
                let x = onRejected(self.reason)
                resolve(x)
            }catch(e){
                reject(e)
            }
        }
        if(self.status === 'pending'){
            self.onResolvedCb.push(function(){
                try{
                    let x = onFulfilled(self.value)
                    resolve(x)
                }catch(e){
                    reject(e)
                }
            })
            self.onRejectedCb.push(function(){
                try{
                    let x = onRejected(self.reason)
                    resolve(x)
                }catch(e){
                    reject(e)
                }
            })
        }
    })
    return promise2
}
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

# 3.3.2 对于then中函数返回值的处理 Promise情况

p.then(v=>{});	// 返回了一个新的Promise实例
1
myPromise.prototype.then = function(onFulfilled, onRejected){
  let self = this
  let promise2 = new myPromise(function(resolve, reject){
    // then 函数的成功回调函数的执行结果 与 promise2的关系
    if(self.status === 'resolved'){
      try{
        let x = onFulfilled(self.value)
        // x可能是一个新的promise , 抽离一个函数来处理x的情况
        // https://juejin.im/post/5b88e06451882542d733767a
        resolvePromise(promise2, x, resolve, reject)
      }catch(e){
       reject(e)
      }
    }
    if(self.status === 'rejected'){...}
    if(self.status === 'pending'){...}
  })
  return promise2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function resolvePromise(promise2, x, resolve, reject) {
    // 防止回调地狱
    if(promise2 === x) {
        return reject(new TypeError('循环引用'))}
    if((typeof x === 'object' || typeof x === 'function') && x){
        try {
            // 每一个Promise都会有then方法,用变量then存储
            let then = x.then;
            // 如果是function,就认为返回的是promise,否则就是一个普通值。
            if (typeof then === 'fucntion') {
                // 注意当前上下文,this不是x
                then.call(x,(y) => {
                    resolve(y)
                }, (e) => {
                    reject(e);
                });
            } else {
                resolve(x);
            }
        } catch (error) {
            reject(error);
        }
    }else {
        resolve(x);
    }
}
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

目前先到这吧~~~只判断了第一次是promise的情况~

// 版本三
function myPromise(executor) {
    let self = this;
    self.status = "pending" //定义状态改变前的初始状态
    self.value = undefined; //定义状态为resolved的时候的状态
    self.reason = undefined; //定义状态为rejected的时候的状态
    
    // 用来保存then 方法中,第一个参数
    self.onResolvedCb = [];
    // 用来保存then 方法中,第二个参数
    self.onRejectedCb = [];
    
    function resolve(value) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
            self.onResolvedCb.forEach(fn => fn());
        }
    }

    function reject(reason) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
            self.onRejectedCb.forEach(fn => fn());
        }
    }
    //捕获构造异常
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    let promise2 = new myPromise(function(resolve, reject){
        // then 函数的成功回调函数的执行结果 与 promise2的关系
        if(self.status === 'resolved'){
            try{
                let x = onFulfilled(self.value);
                // x可能是一个新的promise , 抽离一个函数来处理x的情况
                // https://juejin.im/post/5b88e06451882542d733767a
                resolvePromise(promise2, x, resolve, reject)
            }catch(e){
                reject(e)
            }
        }
        
        if(self.status === 'rejected'){try{
            let x = onRejected(self.reason);
            resolvePromise(promise2, x, resolve, reject)
        }catch(e){
            reject(e)
        }}
        
        if(self.status === 'pending'){// 订阅
            self.onResolvedCb.push(function(){
                onFulfilled(self.value);
            })
            self.onRejectedCb.push(function(){
                onRejected(self.reason);
            })}
    })
    return promise2
}

function resolvePromise(promise2, x, resolve, reject) {
    // 防止回调地狱
    if(promise2 === x) {
        return reject(new TypeError('循环引用'));
    }
    if((typeof x === 'object' || typeof x === 'function') && x){
        try {
            // 每一个Promise都会有then方法,用变量then存储
            let then = x.then;
            // 如果是function,就认为返回的是promise,否则就是一个普通值。
            if (typeof then === 'fucntion') {
                // 注意当前上下文,this不是x
                then.call(x,(y) => {
                    resolve(y)
                }, (e) => {
                    reject(e);
                });
            } else {
                resolve(x);
            }
        } catch (error) {
            reject(error);
        }
    }else {
        resolve(x);
    }
}
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

# 3.4 添加异步

版本三有点乱,在版本二的基础上添加

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    setTimeout(()=>{
        if(self.status === 'resolved'){
            onFulfilled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
    },0);
    
    if(self.status === 'pending'){
        // 订阅
        self.onResolvedCallbacks.push(function(){
            onFulfilled(self.value);
        })
        self.onRejectedCallbacks.push(function(){
            onRejected(self.reason);
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

测试没毛病。

// 面试版本
function myPromise(executor) {
    let self = this;
    self.status = "pending" //定义状态改变前的初始状态
    self.value = undefined; //定义状态为resolved的时候的状态
    self.reason = undefined; //定义状态为rejected的时候的状态
    
    // 用来保存then 方法中,第一个参数
    self.onResolvedCallbacks = [];
    // 用来保存then 方法中,第二个参数
    self.onRejectedCallbacks = [];
    
    function resolve(value) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.value = value;
            self.status = "resolved";
            self.onResolvedCb.forEach(fn => fn());
        }
    }

    function reject(reason) {
        //两个==="pending",保证了状态的改变是不可逆的
        if (self.status === "pending") {
            self.reason = reason;
            self.status = "rejected";
            self.onRejectedCb.forEach(fn => fn());
        }
    }
    //捕获构造异常
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}

myPromise.prototype.then = function (onFulfilled, onRejected) {
    let self = this;
    setTimeout(()=>{
        if(self.status === 'resolved'){
            onFulfilled(self.value);
        }
        if(self.status === 'rejected'){
            onRejected(self.reason);
        }
    },0);
    
    if(self.status === 'pending'){
        // 订阅
        self.onResolvedCallbacks.push(function(){
            onFulfilled(self.value);
        })
        self.onRejectedCallbacks.push(function(){
            onRejected(self.reason);
        })
    }
}
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

# 四、promise的各种用法

https://github.com/sindresorhus/promise-fun

# 4.1 Promise对象实现Ajax操作

var request = (method, url, data) => {
    return new Promise((resolve, reject) => {
        var client = new XMLHttpRequest();
        client.open(method, url);
        client.onreadystatechange = handler;
        client.send(data);

        function handler() {
            if (this.readyState === 4) return;
            if (this.status <= 200 && this.status > 300 || this.status === 304) {
                resolve(this.response);
            }else {
                reject(new Error(this.statusText));
            }
        }

    });

};

request("GET","/get.json").then(
    (json) => {
        console.log(`Contents:${JSON.stringify(json)}`);
    }, (error) => {
        console.error(`Error:${error}`);
    }
);
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
 // 可以避免回调地狱
 // 平常ajax是这样写
 ajax({
     success(){}
 })
1
2
3
4
5

# 五、promise方法

# 5.1 Promise.all()

  • 用于将多个Promise实例包装成一个新的Promise实例。
var p = Promise.all([p1, p2, p3]);
1