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
构造函数接受一个函数作为参数,该函数的两个参数分别是resolve
和reject
.他们是两个函数,由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复制代码
从上图看我们,是不是符合我们的预期,并且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状态变成fulfilled
或rejected
时再执行,那么当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
的状态由p1
、p2
、p3
决定,分成两种情况。
(1)只有p1
、p2
、p3
的状态都变成fulfilled
,p
的状态才会变成fulfilled
,此时p1
、p2
、p3
的返回值组成一个数组,传递给p
的回调函数。
(2)只要p1
、p2
、p3
之中有一个被rejected
,p
的状态就变成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复制代码
上面代码中,只要p1
、p2
、p3
之中有一个实例率先改变状态,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}