Promise详解

Promise

解决回调地狱问题

当我们new一个promise,此时我们需要传递一个回调函数,这个函数为立即执行的,称之为(executor)

这个回调函数,我们需要传入两个参数回调函数,reslove,reject(函数可以进行传参)

  • 当执行了reslove函数,会回调promise对象的.then函数
  • 当执行了reject函数,会回调promise对象的.catche函数

Executor立即执行

1
2
3
4
5
6
7
8
new Promise((resolve, reject) => {
console.log("我是executor函数里面的内容, 我是会立即执行")
resolve(); // 这里如果不调用, .then里面的函数不会执行 reject()同理
}).then( () => {
console.log("调用了resolve方法")
}).catch( () => {
console.log("调用reject方法")
})

image-20230328162439999

Promise状态

使用promise的时候,给它一个承诺,我们可以将他划分为三个阶段

  • pending(待定),执行了executor,状态还在等待中,没有被兑现,也没有被拒绝
  • fulfilled(已兑现),执行了resolve函数则代表了已兑现状态
  • rejected(已拒绝),执行了reject函数则代表了已拒绝状态

首先,状态只要从待定状态,变为其他状态,则状态不能再改变

1
2
3
4
5
6
7
8
9
10
11
new Promise((resolve, reject) => {
reject(); // 两个都调用只会执行一个,因为Promise已经改变
reject(); // 即使调用两次相同的函数,也只会执行一次
resolve();
}).then( () => {
console.log("调用了resolve方法")
}).catch( () => {
console.log("调用reject方法")
})

// 调用reject方法
  • 当我调用reject之后,在调用resolve是无效的,因为状态已经发生改变,并且是不可逆的。

resolve不同值的区别

  • 如果resolve传入一个普通的值或者对象,只能传递接受一个参数,那么这个值会作为then回调的参数

    1
    2
    3
    4
    5
    6
    new Promise((resolve, reject) => {
    resolve({name: '张三', age: 5})
    }).then(res => {
    console.log(res);
    })
    // {name: '张三', age: 5}
  • 如果resolve中传入的是另外一个Promise,那么这个新Promise会决定原Promise的状态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const promise = new Promise((resolve, reject) => {
    resolve(new Promise((resolve, reject) => {
    setTimeout(() => {
    reject('执行失败')
    }, 3000);
    }))
    })

    promise.then(res => console.log(res))
    .catch(err => console.log(err))

    // 打印内容: 执行失败

    上述例子虽然执行的是resolve,但是会触发传入的Promise的reject,从而执行catch方法。所以新传入的Promise会决定原来的Promise状态

  • 如果resolve中传入的是一个对象,并且这个对象有实现then方法,那么会执行该then方法,then方法会传入resolvereject函数。此时的promise状态取决于你调用了resolve,还是reject函数。这种模式也称之为: thenable

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    const promise = new Promise((resolve, reject) => {
    resolve({
    then(res, rej) {
    rej("失败")
    }
    })
    })

    promise.then(res => console.log(res))
    .catch(err => console.log(err))

    // 输出内容:失败

Promise的实例方法

  • 实例方法,存放在Promise.prototype上的方法,也就是Promise的显示原型上,当我new Promise的时候,会把返回的改对象的 promise[[prototype]](隐式原型) === Promise.prototype (显示原型)

  • 即new返回的对象的隐式原型指向了Promise的显示原型

then方法

  • then方法可以接受参数,一个参数为成功的回调,另一个参数为失败的回调

    1
    2
    3
    4
    5
    6
    7
    8
    const promise = new Promise((resolve, reject) => {
    // resolve('request success')
    reject('request error')
    })

    promise.then(res => console.log(res), rej => console.log(rej))

    // 输出内容:request error
  • 如果只捕获错误,还可以这样写

    • 因为第二个参数是捕获异常的,第一个可以写个null""占位
    1
    2
    3
    4
    5
    6
    7
    8
    const promise = new Promise((resolve, reject) => {
    // resolve('request success')
    reject('request error')
    })

    promise.then(null, rej => console.log(rej))

    // 输出内容:request error
  • then方法是有返回值的,它的返回值是promise

    1
    2
    3
    4
    5
    6
    const promise = new Promise((resolve, reject) => {
    resolve('执行成功')
    })

    console.log(promise.then(res => ({name:'张三', age:22})))
    // 返回的是一个Promise对象

    image-20230329100058083

    • 返回一个普通值 状态:fulfilled

      1
      2
      3
      4
      5
      6
      7
      const promise = new Promise((resolve, reject) => {
      resolve('执行成功')
      })

      promise.then(res => ({name:'张三', age:22}))
      .then(res => console.log(res))
      // {name: '张三', age: 22}
      • 返回一个普通值,则相当于主动调用Promise.resolve,并且把返回值作为实参传递到then方法中。

      • 如果没有返回值,则相当于返回undefined

        1
        2
        3
        4
        5
        6
        7
        8
        const promise = new Promise((resolve, reject) => {
        resolve('执行成功')
        })

        promise.then(res => (console.log(111)))
        .then(res => console.log(res))
        // 111
        // undefined
    • 明确返回一个promise状态:fulfilled

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      const promise = new Promise((resolve, reject) => {
      resolve('执行成功')
      })

      promise.then(res => {
      return new Promise((resolve, reject) => {
      resolve('返回的Promise对象resolve里面的内容')
      })
      }).then(res => console.log(res))
      // 返回的Promise对象resolve里面的内容

      返回一个thenable对象 状态:fulfilled

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      const promise = new Promise((resolve, reject) => {
      resolve('hi ice')
      })

      promise.then(res => {
      return {
      then(resolve, reject) {
      resolve('hi webice')
      }
      }
      }).then(res => console.log(res))

      //hi webice

catch方法

  • 如果返回值明确一个promise或者thenable对象,取决于你调用了resolve还是reject

    返回一个普通对象

    1
    2
    3
    4
    5
    6
    7
    const promise = new Promise((resolve, reject) => {
    reject('失败')
    })

    promise.catch(err => ({name:'张三', age: 22}))
    .then(res => console.log(res)) // 如果这里调用.catch,什么也不会输出
    // {name:'张三', age: 22}

    明确返回一个promise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const promise = new Promise((resolve, reject) => {
    reject('ice error')
    })

    promise.catch(err => {
    return new Promise((resolve, reject) => {
    reject('ice error promise')
    })
    }).catch(res => console.log(res))

    //ice error promise

    返回thenable对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const promise = new Promise((resolve, reject) => {
    reject('ice error')
    })

    promise.catch(err => {
    return {
    then(resolve, reject) {
    reject('ice error then')
    }
    }
    }).catch(res => console.log(res))

    //ice error then

finally方法

  • finally(最后),无论promise状态是fulfilled还是rejected都会执行一次finally方法

Promise中的类方法/静态方法

Promise.reslove/Promise.reject

  • 有的时候,你已经预知了状态的结果为fulfilled,则可以用这种简写方式

    1
    2
    3
    Promise.resolve('abc')
    //等价于
    new Promise((resolve, reject) => resolve('abc'))
  • 有的时候,你已经预知了状态的结果为rejected,则可以用这种简写方式

    1
    2
    3
    Promise.reject('error')
    //等价于
    new Promise((resolve, reject) => reject('error'))

Promise.all

fulfilled 状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('111')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('222')
}, 500);
})


Promise.all([promise1, promise2]).then(res => console.log(res))

//[ '111', '222' ]
  • all方法的参数传入为一个可迭代对象,返回一个promise,只有三个都为resolve状态的时候才会调用.then方法。
  • 只要有一个promise的状态为rejected,则会回调.catch方法

rejected状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success1')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('errror1')
}, 2000);
})

const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success2')
}, 3000);
})

Promise.all([promise1, promise2, promise3]).then(res => console.log(res)).catch(err => console.log(err))

// error1
  • 当遇到rejectd的时候,后续的promise结果我们是获取不到,并且会把reject的实参,传递给catch的err形参中

上面的Promise.all有一个缺陷,就是当遇到一个rejected的状态,那么对于后面是resolve或者reject的结果我们是拿不到的

  • ES11 新增语法Promise.allSettled,无论状态是fulfilled/rejected都会把参数返回给我们

Promise.allSettled

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
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success1')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('errror1')
}, 500);
})

const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success2')
}, 3000);
})

Promise.allSettled([promise1, promise2, promise3]).then(res => console.log(res)).catch(err => console.log(err))
// [
// {
// "status": "fulfilled",
// "value": "success1"
// },
// {
// "status": "rejected",
// "reason": "errror1"
// },
// {
// "status": "fulfilled",
// "value": "success2"
// }
// ]
  • 该方法会在所有的Promise都有结果,无论是fulfilled,还是rejected,才会有最终的结果

其中一个promise没有结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success1')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('errror1')
}, 2000);
})

const promise3 = new Promise((resolve, reject) => {
})

Promise.allSettled([promise1, promise2, promise3]).then(res => console.log(res)).catch(err => console.log(err))
// 什么也不打印

Promise.race

  • race(竞争竞赛)
  • 优先获取第一个返回的结果,无论结果是fulfilled还是rejectd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('error1')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success1')
}, 500);
})


Promise.race([promise1, promise2])
.then(res => console.log(res))
.catch(e => console.log(e))
// success1

Promise.any

  • 与race类似,只获取第一个状态为fulfilled,如果全部为rejected则报错AggregateError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('hi error')
}, 1000);
})

const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('hi panda')
}, 2000);
})


Promise.any([promise1, promise2])
.then(res => console.log(res))
.catch(e => console.log(e))

//hi panda

async/await

Promise的语法糖,可以增加代码的可读性(用同步的思维写代码)

await后面的代码都是异步的

  • async(异步的)
  • async 用于申明一个异步函数

async内部代码同步执行

  • 异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行

异步函数的返回值

  • 普通函数主动返回什么就返回什么,不返回为undefined

  • 异步函数的返回值特点

    • 明确有返回一个普通值,相当于Promise.resolve(返回值)
    • 返回一个thenable对象则由,then方法中的resolve,或者reject有关
    • 明确返回一个promise,则由这个promise决定

异步函数的异常处理

  • 如果函数内部中途发生错误,可以通过try catch的方式捕获异常
  • 如果函数内部中途发生错误,也可以通过函数的返回值.catch进行捕获
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

async function sayHi() {
console.log(res)
}
sayHi().catch(e => console.log(e))

//或者

async function sayHi() {
try {
console.log(res)
}catch(e) {
console.log(e)
}
}

sayHi()

//ReferenceError: res is not defined

await 关键字

  • 异步函数中可以使用await关键字,普通函数不行
  • await特点
    • 通常await关键字后面都是跟一个Promise
      • 可以是普通值
      • 可以是thenable
      • 可以是Promise主动调用resolve或者reject
    • 这个promise状态变为fulfilled才会执行await后续的代码,所以await后面的代码,相当于包括在.then方法的回调中,如果状态变为rejected,你则需要在函数内部try catch,或者进行链式调用进行.catch操作

手写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
function LoadImg(src){
return new Promise((resolve, reject) => {
const Img = new Image();
Img.src = src;

Img.onload = () => {
resolve(Img);
}

Img.onerror = () => {
reject("图片加载失败");
}
});
}
LoadImg('https://robohash.org/1')
.then((Img) => {
document.body.appendChild(Img);
return LoadImg('https://robohash.org/2')
})
.then((Img) => {
document.body.appendChild(Img);
return LoadImg('https://robohash.org/3')
})
.then((Img) => {
document.body.appendChild(Img);
})
.catch((errMsg) => {
console.log(errMsg);
})

image-20230328155141667

参考文章

https://juejin.cn/post/7144308012952322084

Promise阅读代码题:https://juejin.cn/post/6844904077537574919


Promise详解
http://example.com/2023/03/28/07.前端小课堂/08.Promise详解/
作者
Deng ErPu
发布于
2023年3月28日
许可协议