promise是什么?主要用来解决什么问题?

Promise是异步编程的一种解决方案,比传统解决方案–回调函数和事件–更合理更强大。

Promise特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功)和reject(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其它操作都无法改变这个状态。这也是Promise(承诺)这个名字的由来。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected

promise主要用来解决:

  • 回调地狱
  • 并发请求
  • 异步方案优化(但它本身不是异步的,new Promise()后,它会立即执行)

promise基本用法

ES6规定,Promise对象是一个构造函数,用来生成Promise实例

下面代码创造了一个Promise实例

 1const promise = new Promise(function(resolve,reject){
 2    //...
 3    if(/*异步操作成功*/){
 4       resolve(value)
 5    }else{
 6        //异步操作失败
 7        reject(error)
 8    }
 9})
10复制代码

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject.他们是两个函数,由JavaScript引擎提供,不用自己部署。

resolve函数的作用是,将Promise对象的状态从“未完成”变成“成功”(即从pending变为resolve),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变成“失败”(即从pending变为rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数,或用catch方法指定rejected状态的回调函数。

 1promise.then(res=>{
 2    //success
 3},error=>{
 4    //error
 5}).catch(err=>{})
 6复制代码

then方法可以接受两个回调函数作为参数,第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接收Promise对象传出的值作为参数。

Ok,通过上面对promise基本用法的描述,我们大概知道了一个promise类里面都应该包含哪些内容了:

  • promise状态:pending,fulfilled,rejected

  • promise返回值

  • 执行器:promise执行入口(也就是你传入的那个函数)

  • resolve:改变promise状态为fulfilled

  • reject:改变promise状态为rejected

  • then:接收两个回调,onFulfilled, onRejected。分别在promise状态变为fulfiled或rejected后执行

  • catch:接受一个回调,在promise状态变为rejected后执行

简单实现一个promise

我们知道了一个promise内容至少包含以上那些内容,所以一个简单的promise内部至少是这样的

 1class myPromise {
 2    static PENDING = 'pending'
 3    static FULFILLEd = 'fulfilled'
 4    static REJECTED = 'rejected'
 5    constructor(init){
 6        this.state = myPromise.PENDING // promise状态
 7        this.promiseRes = null  // promise返回值
 8       	const resolve = result=>{
 9        //...
10        }
11        const reject = result=>{
12           //...
13        }
14        try{
15            init(resolve,reject)  // init就是初始化执行器
16        }catch(err){
17            reject(err)
18        }
19        
20    }
21    then(onFulfilled,onRejected){
22       //...
23    }
24  	catch(onRejected){
25      //...
26    }
27}
28复制代码

OK,大概了解之后,我们再来一个一个的看里面每个部分的实现以及作用

Promise的执行器

它其实是我们在new Promise时传入的一个回调函数,这个函数本身是同步的,也就是说在new Promise时它就会执行,这也是我们操作promise的入口。

 1class myPromise{
 2  //...
 3  constructor(init){
 4        try{
 5            init(resolve,reject)  // init就是初始化执行器
 6        }catch(err){
 7            reject(err) //这里主要是在init执行器函数出错时,用以让promise状态变为rejected
 8        } 
 9    }
10  //...
11}
12复制代码

该函数接受两个回调函数(resolve,reject)作为参数,用以改变Promise的状态

resolve与reject方法

这两个函数作为参数传到执行器函数中,用以后续改变Promise状态

 1class myPromise {
 2    static PENDING = 'pending'
 3    static FULFILLEd = 'fulfilled'
 4    static REJECTED = 'rejected'
 5    constructor(init){
 6        this.state = myPromise.PENDING // promise状态
 7        this.promiseRes = null  // promise返回值
 8        this.resolveCallback = [] //成功回调集合
 9        this.rejectCallback = [] //失败回调集合
10        const resolve = result=>{
11            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
12            if(this.state === myPromise.PENDING){
13                this.state = myPromise.FULFILLEd //改变状态
14                this.promiseRes = result //返回值
15                //依次调用成功回调
16                this.resolveCallback.forEach(fn=>fn())
17            }
18        }
19        const reject = result=>{
20            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
21            if(this.state === myPromise.PENDING){
22                this.state = myPromise.REJECTED //改变状态
23                this.promiseRes = result //返回值
24                // 依次调用失败回调
25                this.rejectCallback.forEach(fn=>fn())
26            }
27        }
28        try{
29            init(resolve,reject)  // 注意this指向
30        }catch(err){
31            reject(err)
32        }
33        
34    }
35}
36复制代码

初步then方法

 1class myPromise {
 2    static PENDING = 'pending'
 3    static FULFILLEd = 'fulfilled'
 4    static REJECTED = 'rejected'
 5    constructor(init){
 6        this.state = myPromise.PENDING // promise状态
 7        this.promiseRes = null  // promise返回值
 8        this.resolveCallback = [] //成功回调集合
 9        this.rejectCallback = [] //失败回调集合
10        const resolve = result=>{
11            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
12            if(this.state === myPromise.PENDING){
13                this.state = myPromise.FULFILLEd //改变状态
14                this.promiseRes = result //返回值
15                //依次调用成功回调
16                this.resolveCallback.forEach(fn=>fn())
17            }
18        }
19        const reject = result=>{
20            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
21            if(this.state === myPromise.PENDING){
22                this.state = myPromise.REJECTED //改变状态
23                this.promiseRes = result //返回值
24                // 依次调用失败回调
25                this.rejectCallback.forEach(fn=>fn())
26            }
27        }
28        try{
29            init(resolve,reject)  // 注意this指向
30        }catch(err){
31            reject(err)
32        }
33        
34    }
35    then(onFulfilled,onRejected){
36        if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {
37            onFulfilled(this.promiseRes)
38        }
39        if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {
40            onRejected(this.promiseRes)
41        }
42    }
43}
44复制代码

写到这里,我们的promise已经初步成型了,我们可以来测试一下:

 1const res1 = new myPromise((res,rej)=>{
 2    res('成功啦~')
 3    rej('失败啦~')
 4})
 5res1.then((res)=>{
 6    console.log(res)
 7},err=>{
 8    console.log(err)
 9})
10// 按照预期,这里应该是只会打印出成功啦~
11复制代码

promise1.png

从上图看我们,是不是符合我们的预期,并且myPromise内部与原生的Promise也是非常相似的。你们是不是觉得这里已经没问题了,上面我们只是测了一下同步方法的执行,但别忘了,Promise主要是来解决异步问题的,我们再来试一下里面执行异步方法还符不符合我们的预期?

 1const res1 = new myPromise((res,rej)=>{
 2    setTimeout(()=>res('成功啦~'),1000)
 3    // rej('失败啦~')
 4})
 5res1.then((res)=>{
 6    console.log(res)
 7})
 8复制代码

这里我们预期本来是一秒之后打印成功啦,但它并没有如我们所愿,反而是什么也没打印出来,这是因为在setTimeout执行之前(pen ding)这个then方法已经执行过了,1s后状态变成fulfilled时,then也不会再执行了。

所以我们需要保证then方法的回调函数在promise状态变成fulfilledrejected时再执行,那么当promise状态为pending时我们先要把回调存在对应的队列中,等后续状态改变后再执行

较完整then方法

OK,这里我们修改一下我们的then方法,让其保证异步代码执行的正确性 (具体实现微任务我们可以用 mutationObserver,这里我们就用setTimeout来模拟一下)

 1class myPromise {
 2    static PENDING = 'pending'
 3    static FULFILLEd = 'fulfilled'
 4    static REJECTED = 'rejected'
 5    constructor(init){
 6        this.state = myPromise.PENDING // promise状态
 7        this.promiseRes = null  // promise返回值
 8        this.resolveCallback = [] //成功回调集合
 9        this.rejectCallback = [] //失败回调集合
10        const resolve = result=>{
11            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
12            if(this.state === myPromise.PENDING){
13                this.state = myPromise.FULFILLEd //改变状态
14                this.promiseRes = result //返回值
15                //依次调用成功回调
16                this.resolveCallback.forEach(fn=>fn())
17            }
18        }
19        const reject = result=>{
20            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
21            if(this.state === myPromise.PENDING){
22                this.state = myPromise.REJECTED //改变状态
23                this.promiseRes = result //返回值
24                // 依次调用失败回调
25                this.rejectCallback.forEach(fn=>fn())
26            }
27        }
28        try{
29            init(resolve,reject)  // 注意this指向
30        }catch(err){
31            reject(err)
32        }
33        
34    }
35    then(onFulfilled,onRejected){
36        if(this.state === myPromise.FULFILLEd && typeof onFulfilled === 'function') {
37            onFulfilled(this.promiseRes)
38        }
39        if(this.state === myPromise.REJECTED && typeof onRejected === 'function') {
40            onRejected(this.promiseRes)
41        }
42        if(this.state === myPromise.PENDING){
43            if(onFulfilled && typeof onFulfilled === 'function'){
44                this.resolveCallback.push(()=>
45                // 这里我们用setTimeout来模拟实现then的微任务
46                setTimeout(()=>{
47                    onFulfilled(this.promiseRes)
48                },0)
49                )
50            }
51            if(onRejected && typeof onRejected === 'function'){
52                this.rejectCallback.push(()=>
53                // 这里我们用setTimeout来模拟实现then的微任务
54                setTimeout(()=>{
55                    onRejected(this.promiseRes)
56                },0)
57                )
58            }
59        }
60    }
61}
62复制代码

这里我们可以再测试一下上面那个异步函数的测试用例,发现它能够正确打印,OK,一个较完整的then方法就算实现了~

then的链式调用

then方法会返回一个新的Promise(⚠️注意:不是原来的那个Promise)所以可以采用链式调用

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

 1then(onFulfilled,onRejected){
 2        const {promiseRes,state} = this
 3        let promise = new myPromise((reso,reje)=>{
 4            const resolveMyPromise = promiseRes => {
 5                try{
 6                    if(typeof onFulfilled !== 'function'){
 7                        // 如果then的第一个回调不是一个函数,直接忽略,返回一个新的promise
 8                        reso(promiseRes)
 9                    }else{
10                        // 获取第一个回调的执行结果
11                        const res = onFulfilled(promiseRes)
12                        // 看该执行结果是否是一个promise
13                        if(res instanceof myPromise){
14                            // 是一个promise,等它状态改变后再改变then返回的promise状态
15                            res.then(reso,rej) 
16                        }else{
17                            // 不是一个promise,将它作为新的promise的resolve
18                            reso(res)
19                        }
20                    }
21                }catch(err){
22                    //异常,直接将新的promise状态置为rejected
23                    reje(err)
24                }
25            }
26            const rejectMyPromise = promiseRes => {
27                try{
28                    if(typeof onRejected !== 'function'){
29                        // 如果then的第二个回调不是一个函数,直接忽略,返回一个新的promise
30                        reje(promiseRes)
31                    }else{
32                        // 获取第二个回调的执行结果
33                        const res = onRejected(promiseRes)
34                        // 看该执行结果是否是一个promise
35                        if(res instanceof myPromise){
36                            // 是一个promise,等它状态改变后再改变then返回的promise状态
37                            res.then(reso,rej) 
38                        }else{
39                            // 不是一个promise,将它作为新的promise的resolve
40                            reje(res)
41                        }
42                    }
43                    
44                }catch(err){
45                    //异常,直接将新的promise状态置为rejected
46                    reje(err)
47                }
48            }
49            if(state === myPromise.FULFILLEd) {
50                resolveMyPromise(promiseRes)
51            }
52            if(state === myPromise.REJECTED) {
53                rejectMyPromise(promiseRes)
54            }
55            if(state === myPromise.PENDING){
56                if(onFulfilled && typeof onFulfilled === 'function'){
57                    this.resolveCallback.push(()=>
58                    // 这里我们用setTimeout来模拟实现then的微任务
59                    setTimeout(()=>{
60                        resolveMyPromise(this.promiseRes)
61                    },0)
62                    )
63                }
64                if(onRejected && typeof onRejected === 'function'){
65                    this.rejectCallback.push(()=>
66                    // 这里我们用setTimeout来模拟实现then的微任务
67                    setTimeout(()=>{
68                        rejectMyPromise(this.promiseRes)
69                    },0)
70                    )
71                }
72            }
73
74        })
75        return promise
76    }
77复制代码

catch方法

我们知道then的第二个回调其实与catch方法是一样的,所以catch方法我们可以这样实现

 1catch(onRejected) {
 2        return this.then(undefined,onRejected)
 3    }
 4复制代码

Promise.resolve

将对象转为一个promise对象,根据参数不通可分为四种情况

  • 参数是一个Promise实例,直接返回该实例
  • 参数是一个thenable对象,将该对象转为Promise对象后,执行该对象的then方法
  • 没有参数,也是返回一个状态为resolved的新的Promise对象
  • 参数是一个一个原始值,返回一个新的Promise对象,状态为resolved

手动实现:

 1static resolve(v){
 2  //1.参数是一个Promise实例,直接返回
 3  if(v instanceof myPromise){
 4    return v
 5  }
 6  //2.参数是一个thenable对象,转为Promise后执行该对象的then方法
 7  if(typeof v === 'object' && typeof v.then === 'function'){
 8    return new myPromise((res,rej)=>{
 9      v.then(res,rej)
10    })
11  }
12  //3.没有参数,直接返回一个resolved状态的promise
13  if(!v){
14    return new myPromise(res=>{
15      res()
16    })
17  }
18  //4.参数是一个原始值,返回一个新的Promise,状态为resolved
19  return new myPromise(res=>{
20    res(v)
21  })
22}
23复制代码

Promise.reject

返回一个新的Promise对象,状态为rejected

 1static reject(v){
 2  return new myPromise((res,rej)=>{
 3    rej(v)
 4  })
 5}
 6复制代码

Promise.all

该方法用于将多个Promise实例包装成一个新的Promise实例,如果有不是Promise的项,则让该项直接成功

用法:

 1const p = Promise.all([p1,p2,p3])
 2复制代码

p的状态由p1p2p3决定,分成两种情况。

(1)只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

Ok,了解完Promise.all我们动手来实现一遍

手动实现:

 1static all (promises){
 2        return new myPromise((res,rej)=>{
 3            let count = 0
 4            const result = [];
 5            function addFun(index,resf) {
 6                result[index]=resf // 这里用索引别用push,保证返回的顺序
 7                count++
 8                if(count==promises.length) {
 9                    res(result)
10                }
11            }
12            [].forEach.call(promises,(promise,index)=>{
13                if(promise instanceof myPromise) {
14                    promise.then(success=>{
15                        // count ++
16                        // result.push(success)
17                        addFun(index,success)
18                    },err=>{
19                        rej(err)
20                    })
21                }else{
22                    addFun(index,promise)
23                }
24            })
25        })
26    }
27复制代码

Promise.race

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

用法:

 1const p = Promise.race([p1, p2, p3]);
 2复制代码

上面代码中,只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

手动实现:

 1static race(promises) {
 2        return new myPromise((res,rej)=>{
 3            [].forEach.call(promises,promise=>{
 4                if(promise instanceof myPromise){
 5                    promise.then(success=>{
 6                        res(success)
 7                    },error=>{
 8                        rej(error)
 9                    })
10                }else{
11                    res(promise)
12                } 
13            })
14        })
15    }
16复制代码

完整代码

 1class myPromise {
 2    static PENDING = 'pending'
 3    static FULFILLEd = 'fulfilled'
 4    static REJECTED = 'rejected'
 5    constructor(init){
 6        this.state = myPromise.PENDING // promise状态
 7        this.promiseRes = null  // promise返回值
 8        this.resolveCallback = [] //成功回调集合
 9        this.rejectCallback = [] //失败回调集合
10        const resolve = result=>{
11            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
12            if(this.state === myPromise.PENDING){
13                this.state = myPromise.FULFILLEd //改变状态
14                this.promiseRes = result //返回值
15                //依次调用成功回调
16                this.resolveCallback.forEach(fn=>fn())
17            }
18        }
19        const reject = result=>{
20            // 只有当状态为pending时才改变,保证状态一旦改变就不会再变
21            if(this.state === myPromise.PENDING){
22                this.state = myPromise.REJECTED //改变状态
23                this.promiseRes = result //返回值
24                // 依次调用失败回调
25                this.rejectCallback.forEach(fn=>fn())
26            }
27        }
28        try{
29            init(resolve,reject)  // 注意this指向
30        }catch(err){
31            reject(err)
32        }
33        
34    }
35    then(onFulfilled,onRejected){
36        const {promiseRes,state} = this
37        let promise = new myPromise((reso,reje)=>{
38            const resolveMyPromise = promiseRes => {
39                try{
40                    if(typeof onFulfilled !== 'function'){
41                        // 如果then的第一个回调不是一个函数,直接忽略,返回一个新的promise
42                        reso(promiseRes)
43                    }else{
44                        // 获取第一个回调的执行结果
45                        const res = onFulfilled(promiseRes)
46                        // 看该执行结果是否是一个promise
47                        if(res instanceof myPromise){
48                            // 是一个promise,等它状态改变后再改变then返回的promise状态
49                            res.then(reso,rej) 
50                        }else{
51                            // 不是一个promise,将它作为新的promise的resolve
52                            reso(res)
53                        }
54                    }
55                }catch(err){
56                    //异常,直接将新的promise状态置为rejected
57                    reje(err)
58                }
59            }
60            const rejectMyPromise = promiseRes => {
61                try{
62                    if(typeof onRejected !== 'function'){
63                        // 如果then的第二个回调不是一个函数,直接忽略,返回一个新的promise
64                        reje(promiseRes)
65                    }else{
66                        // 获取第二个回调的执行结果
67                        const res = onRejected(promiseRes)
68                        // 看该执行结果是否是一个promise
69                        if(res instanceof myPromise){
70                            // 是一个promise,等它状态改变后再改变then返回的promise状态
71                            res.then(reso,rej) 
72                        }else{
73                            // 不是一个promise,将它作为新的promise的resolve
74                            reje(res)
75                        }
76                    }
77                    
78                }catch(err){
79                    //异常,直接将新的promise状态置为rejected
80                    reje(err)
81                }
82            }
83            if(state === myPromise.FULFILLEd) {
84                resolveMyPromise(promiseRes)
85            }
86            if(state === myPromise.REJECTED) {
87                rejectMyPromise(promiseRes)
88            }
89            if(state === myPromise.PENDING){
90                if(onFulfilled && typeof onFulfilled === 'function'){
91                    this.resolveCallback.push(()=>
92                    // 这里我们用setTimeout来模拟实现then的微任务
93                    setTimeout(()=>{
94                        resolveMyPromise(this.promiseRes)
95                    },0)
96                    )
97                }
98                if(onRejected && typeof onRejected === 'function'){
99                    this.rejectCallback.push(()=>
100                    // 这里我们用setTimeout来模拟实现then的微任务
101                    setTimeout(()=>{
102                        rejectMyPromise(this.promiseRes)
103                    },0)
104                    )
105                }
106            }
107
108        })
109        return promise
110    }
111    catch(onRejected) {
112        return this.then(undefined,onRejected)
113    }
114    static all (promises){
115        return new myPromise((res,rej)=>{
116            let count = 0
117            const result = [];
118            function addFun(index,resf) {
119                result[index]=resf // 这里用索引别用push,保证返回的顺序
120                count++
121                if(count==promises.length) {
122                    res(result)
123                }
124            }
125            [].forEach.call(promises,(promise,index)=>{
126                if(promise instanceof myPromise) {
127                    promise.then(success=>{
128                        addFun(index,success)
129                    },err=>{
130                        rej(err)
131                    })
132                }else{
133                    addFun(index,promise)
134                }
135            })
136        })
137    }
138    static race(promises) {
139        return new myPromise((res,rej)=>{
140            [].forEach.call(promises,promise=>{
141                if(promise instanceof myPromise){
142                    promise.then(success=>{
143                        res(success)
144                    },error=>{
145                        rej(error)
146                    })
147                }else{
148                    res(promise)
149                } 
150            })
151        })
152    }
153    static resolve(v){
154        //1.参数是一个Promise实例,直接返回
155        if(v instanceof myPromise){
156            return v
157        }
158        //2.参数是一个thenable对象,转为Promise后执行该对象的then方法
159        if(typeof v === 'object' && typeof v.then === 'function'){
160            return new myPromise((res,rej)=>{
161                v.then(res,rej)
162            })
163        }
164        //3.没有参数,直接返回一个resolved状态的promise
165        if(!v){
166            return new myPromise(res=>{
167                res()
168            })
169        }
170        //4.参数是一个原始值,返回一个新的Promise,状态为resolved
171        return new myPromise(res=>{
172            res(v)
173        })
174    }
175    static reject(v){
176        return new myPromise((res,rej)=>{
177            rej(v)
178        })
179    }
180}
个人笔记记录 2021 ~ 2025