详解Promise

2022/3/9 JavaScriptPromise

# 异步任务的处理

# 使用回调函数的方式请求

function requestData(url, resCallback, errCallback) {
 setTimeout(() => {
   if (url == "okarin") {
     let res = "Hello World!"
     resCallback(res)
   } else {
     let err = "Error"
     errCallback(err)
   }
 },2000)
}

requestData(
 "okarin",
 (res) => {
   console.log(res)
 },
 (err) => {
   console.log(err)
 }
)

requestData(
   "retr0",
   (res) => {
     console.log(res)
   },
   (err) => {
     console.log(err)
   }
 )
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

这种请求的弊端:

  1. 我们需要自己来设计回调函数、回调函数的名称、回调函数的使用等;
  2. 对于不同的人、不同的框架设计出来的方案是不同的,那么我们必须耐心去看别人的源码或者文档,以便可以理解它这个函数到底怎么用;

# 使用Promise的方式

我们来看一下Promise的API是怎么样的:

  • Promise是一个类,可以翻译成 承诺、许诺、期约;

  • 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个Promise的对象;

  • 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor

    • 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject;
    • 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
    • 当我们调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数;
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url == "okarin") {
        let res = "Hello World!"
        resolve(res)
      } else {
        let err = "Error"
        reject(err)
      }
    }, 2000)
  })
}

requestData("okarin")
  .then((res) => {
    console.log(res)
  })
  .catch((err) => {
    console.log(err)
  })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# Promise的代码结构

new Promise((resolve, reject) => {
  setTimeout(() => {
      //pending
    resolve("succeed") //成功的时候调用
    reject("error") //失败的时候调用
  }, 1000)
})
  .then((resolve) => {
    //fulfilled
    //成功之后执行
    console.log(resolve)
  })
  .catch((err) => {
    //rejected
    //失败之后执行
    console.log(err)
  })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Promise 三种状态

  • pending: 等待状态,比如正在进行网络请求,或者定时器没有到时间
  • fulfill: 满足状态,当主动回调resolve时,就处于该状态,并且会回调.then()
  • reject: 拒绝状态,当主动回调reject时,就处于该状态,并且会回调.catch()

注意:状态一旦确定,就不能更改。

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("succeed")
    console.log("状态确定")
    reject("error")
  }, 1000)
})
  .then((resolve) => {
    console.log(resolve)
  })
  .catch((err) => {
    console.log(err)
  })

//succeed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Promise参数

resolve不同值的区别

  1. 如果resolve传入一个普通的值或者对象,那么这个值会作为then回调的参数;
  2. 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态:
  3. 如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,并且根据then方法的结果来决定Promise的状态
const newPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success")
  }, 1000)
})

new Promise((resolve, reject) => {
  setTimeout(() => {
      //传入一个新的Promise
    resolve(newPromise) //成功的时候调用
  }, 1000)
})
  .then((resolve) => {
    //成功之后执行
    console.log("success:" + resolve)
  })
  .catch((err) => {
    //失败之后执行
    console.log(err)
  })

//success:success
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const obj = {
    then:function(resolve,reject){
        resolve("thenable")
    }
}

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(obj) //成功的时候调用
  }, 1000)
})
  .then((resolve) => {
    //成功之后执行
    console.log("success:" + resolve)
  })
  .catch((err) => {
    //失败之后执行
    console.log(err)
  })

//success:thenable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Promise 实例方法

Promise的实例方法,是存放在Promise的prototype上的方法。

const promise = new Promise((resolve, reject) => {
  resolve()
//   reject(err)
})

promise.then(()=>{
    console.log("resolve")
})
promise.catch(()=>{
    console.log("reject")
})
promise.finally(()=>{
       console.log("finally code ")
   })
1
2
3
4
5
6
7
8
9
10
11
12
13
14

我们可以获取一下原型上的方法

console.log(Object.getOwnPropertyDescriptors(Promise.prototype))
1

结果

// {
//     constructor: {
//       value: [Function: Promise],
//       writable: true,
//       enumerable: false,
//       configurable: true
//     },
//     then: {
//       value: [Function: then],
//       writable: true,
//       enumerable: false,
//       configurable: true
//     },
//     catch: {
//       value: [Function: catch],
//       writable: true,
//       enumerable: false,
//       configurable: true
//     },
//     finally: {
//       value: [Function: finally],
//       writable: true,
//       enumerable: false,
//       configurable: true
//     },
//       value: 'Promise',
//       writable: false,
//       enumerable: false,
//       configurable: true
//     }
//   }
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

# then 方法

then方法是Promise对象上的一个方法:它其实是放在Promise的原型上的Promise.prototype.then ,then 方法接受两个参数:

  • fulfilled的回调函数:当状态变成fulfilled时会回调的函数;
  • reject的回调函数:当状态变成reject时会回调的函数;
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url == "okarin") {
        let res = "Hello World!"
        resolve(res)
      } else {
        let err = "Error"
        reject(err)
      }
    }, 2000)
  })
}

requestData().then(
  (res) => {
    console.log("succeed:" + res)
  },
  (err) => {
    //失败之后执行
    console.log("Error:" + err)
  }
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

一个Promise的then方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的fulfilled回调;
  • 当Promise的状态变成fulfilled的时候,这些回调函数都会被执行;
promise.then((res) => {
  console.log("res1:" + res)
})
promise.then((res) => {
  console.log("res2:" + res)
})
promise.then((res) => {
  console.log("res3:" + res)
})
1
2
3
4
5
6
7
8
9

then方法本身是有返回值的,它的返回值是一个Promise,所以我们可以进行如下的链式调用:

  • 但是then方法返回的Promise到底处于什么样的状态呢?

Promise有三种状态,那么这个Promise处于什么状态呢?

  • 当then方法中的回调函数本身在执行的时候,那么它处于pending状态;

  • 当then方法中的回调函数返回一个结果时,那么它处于fulfilled状态,并且会将结果作为resolve的参数;

    • 情况一:返回一个普通的值;

    • 情况二:返回一个Promise;

    • 情况三:返回一个thenable值;

  • 当then方法抛出一个异常时,那么它处于reject状态;

# catch方法

catch方法也是Promise对象上的一个方法:

它也是放在Promise的原型上的 Promise.prototype.catch 一个Promise的catch方法是可以被多次调用的:

  • 每次调用我们都可以传入对应的reject回调;
  • 当Promise的状态变成reject的时候,这些回调函数都会被执行;
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url == "okarin") {
        let res = "Hello World!"
        resolve(res)
      } else {
        let err = "Error"
        reject(err)
      }
    }, 2000)
  })
}

requestData().catch((err) => {
    //失败之后执行
    console.log("catch1:" + err)
  })

requestData().catch((err) => {
  //失败之后执行
  console.log("catch2:" + err)
})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# catch方法的返回值

catch方法也是会返回一个Promise对象,所以catch方法后面我们可以继续调用then方法或者catch方法

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url == "okarin") {
        let res = "Hello World!"
        resolve(res)
      } else {
        let err = "Error"
        reject(err)
      }
    }, 2000)
  })
}

requestData()
  .catch((err) => {
    //失败之后执行
    console.log("catch:" + err)
    return "catch return value"
  })//继续调用then方法和catch方法
  .then((res) => {
    console.log("succeed:" + res)
  })
  .catch((err) => {
    //失败之后执行
    console.log("catch2:" + err)
  })

//catch:Error
//succeed:catch return value
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

在上面的代码中,在catch后面继续调用then方法和catch方法。回调的是then方法。

这是因为 catch 传入的回调在执行完后,默认状态依然会是fulfilled的;相当于

new Promise(resolve => resolve(x))
1

要继续调用catch,就需要继续抛出异常

requestData()
  .catch((err) => {
    //失败之后执行
    console.log("catch:" + err)
    return new Promise((resolve, reject) => {
      reject(err)
    })
  })
  .then((res) => {
    console.log("succeed:" + res)
  })
  .catch((err) => {
    //失败之后执行
    console.log("catch2:" + err)
  })

//catch:Error
//catch2:Error
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# finally方法 ES9新增

finally是在 ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成 fulfilled 还是 rejected 状态,最终都会被执行的代码。

finally方法是不接收参数的,因为无论前面是fulfilled状态,还是rejected状态,它都会执行。

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url == "okarin") {
        let res = "Hello World!"
        resolve(res)
      } else {
        let err = "Error"
        reject(err)
      }
    }, 2000)
  })
}

requestData("okarin")
  .then((res) => {
    console.log("succeed:" + res)
  })
  .catch((err) => {
    //失败之后执行
    console.log("catch:" + err)
  }).finally(()=>{
      console.log("finally code ")
  })

//succeed:Hello World!
//finally code 
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类方法

# resolve方法

有时候我们已经有一个现成的内容了,希望将其转成Promise来使用

function foo(){
   const obj = {name:"okarin"}
   return new Promise((resolve)=>{
       resolve(obj)
   })
}

foo().then(res =>{
   console.log("res:",res)
})
1
2
3
4
5
6
7
8
9
10

这个时候我们可以使用 Promise.resolve方法来完成。Promise.resolve的用法相当于new Promise,并且执行resolve操作:

function foo(){
    const obj = {name:"okarin"}
    return Promise.resolve(obj)
}

foo().then(res =>{
    console.log("res:",res)
})

1
2
3
4
5
6
7
8
9

同时,当resolve传入的是 promise 或者 thenable 时,会根据promise和thenable来回调。

const newPromise = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("success")
    }, 1000)
  })

function foo() {
  const obj = { name: "okarin" }
  return Promise.resolve(newPromise)
}

foo()
  .then((res) => {
    console.log("res:", res)
  })
  .catch((err) => {
    console.log("err:", err)
  })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# reject方法

function foo() {
  const obj = { name: "okarin" }
  return Promise.reject(obj)
}

foo()
  .then((res) => {
    console.log("res:", res)
  })
  .catch((err) => {
    console.log("err:", err)
  })

1
2
3
4
5
6
7
8
9
10
11
12
13

注意:无论reject传入什么值,都是一样的。

传入一个Promise

const newPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
   reject("Error")
  }, 1000)
})

function foo() {
const obj = { name: "okarin" }
return Promise.reject(newPromise)
}

foo()
.then((res) => {
  console.log("res:", res)
})
.catch((err) => {
  console.log("err:", err)
})

//err: Promise { <pending> }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

传入一个Function

const newPromise = function(resolve, reject){
  setTimeout(() => {
   reject("Error")
  }, 1000)
}

function foo() {
const obj = { name: "okarin" }
return Promise.reject(newPromise)
}

foo()
.then((res) => {
  console.log("res:", res)
})
.catch((err) => {
  console.log("err:", err)
})

//err: [Function: newPromise]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# all方法

在所有的Promise都变成fulfilled时,再拿到结果

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(18)
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("chengdu")
  }, 3000)
})

const message = "Hello World!"

Promise.all([p1, p2, p3, message]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//[ 'okarin', 18, 'chengdu', 'Hello World!' ]
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变成 rejected状态时,新的Promise就会立即变成对应的reject状态。而对于resolved,以及依然处于pending状态的Promise,则获取不到对应的结果。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("ERROR") //ERROR
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")//ERROR 拿不到第二次错误
  }, 3000)
})

const message = "Hello World!"

Promise.all([p1, p2, p3, message]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//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

# allSettled方法 ES11新增

为了解决all方法的缺陷,在ES11(ES2020)中,添加了新的API Promise.allSettled

  • 该方法会在所有的Promise都有结果(settled)时,无论是fulfilled,还是rejected时,才会有最终的状态。
  • 这个Promise的结果一定是fulfilled的。
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("ERROR") //ERROR
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")
  }, 3000)
})

const message = "Hello World!"

Promise.allSettled([p1, p2, p3, message]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

// [
//   { status: 'fulfilled', value: 'okarin' },
//   { status: 'rejected', reason: 'ERROR' },
//   { status: 'rejected', reason: 'SECEND ERROR' },
//   { status: 'fulfilled', value: 'Hello World!' }
// ]
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

### race方法

如果一个Promise有了结果,希望用这个结果来决定最终Promise的状态,那么就可以用race方法。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("ERROR") //ERROR
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")
  }, 3000)
})


Promise.race([p1, p2, p3]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//okarin
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

注意:如果有一个Promise的状态先变成了rejected,那么新的Promise也会变成rejected状态。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("ERROR") //ERROR
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")
  }, 500) //先触发
})


Promise.race([p1, p2, p3]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//SECEND 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

# any方法 ES12新增

any方法是ES12中新增的方法,和 race方法类似;

  • any方法会等到一个fulfilled状态,才会决定新Promise的状态;
  • 如果所有的Promise都是reject,那么也会等到所有的Promise变成rejected状态;
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("okarin")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("ERROR") //ERROR
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")
  }, 3000)
})


Promise.any([p1, p2, p3]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//okarin
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

所有的Promise都是reject

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("FIRST ERROR")
  }, 1000)
})

const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("SECEND ERROR")  
  }, 2000)
})

const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("THIRD ERROR")
  }, 3000)
})


Promise.any([p1, p2, p3]).then((res) => {
  console.log(res)
}).catch(err => {
  console.log(err)
})

//AggregateError: All promises were rejected
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

# Promise的简单实现

const PROMISE_STASUS_PENDING = "pending"
const PROMISE_STASUS_FULFILLED = "fulfilled"
const PROMISE_STASUS_REJECTED = "rejected"

class onPromise {
 constructor(excutor) {
   this.status = PROMISE_STASUS_PENDING
   const resolve = (value) => {
     if (this.status === PROMISE_STASUS_PENDING) {
       this.status = PROMISE_STASUS_FULFILLED
       queueMicrotask(() => {
         this.onFulfilled(value)
       })
     }
   }
   const reject = (reason) => {
     if (this.status === PROMISE_STASUS_PENDING) {
       this.status = PROMISE_STASUS_REJECTED
       queueMicrotask(() => {
         this.onRejected(reason)
       })
     }
   }
   excutor(resolve, reject)
 }
 then(onFulfilled, onRejected) {
   this.onFulfilled = onFulfilled
   this.onRejected = onRejected
 }
}


const promise = new onPromise((resolve, reject) => {
 resolve(1111)
 // reject()
}).then((res) => {
 console.log("res:" + res)
}, (err) => {
 console.log("err:" + err)
})
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
Last Updated: 2022/3/9下午11:43:31