你了解Promise么

230 阅读5分钟

Promise是面试官经常问的一个问题

也算是基础知识点,如果这个不了解的话,他可能会认为你还不能快速的进入项目开发当中来。

1 什么是Promise

Promise是一种异步编程的解决方案,为了解决回调地狱的问题。 回调地狱就是一个异步请求套着另外一个异步请求,依赖其执行结果层层嵌套。Promise是一个对象,从它中可以获取异步操作的消息。

2 Promise的特点

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

var promise = new Promise(executor){

}

正如上,构建一个Promise对象时,需要传入一个executor函数,主要的业务流程在executor中执行。

Promise构造函数执行时,立即调用executor函数,其中resolve和reject两个函数作为参数传递给executor。resolve和reject调用时,分别将Promise状态由 pengding => fulfilled pending => rejected 且状态一旦改变,则不会再次改变。

executor函数中调用resolve函数,则触发Promise.then设置的回调函数,调用reject函数,则触发Promise.catch设置的回调函数。

3 同步还是异步

Promise是用来管理异步编程的,它本身是同步的。 例:

let p1 = new Promise(() => {
  setTimeout(() => {
    console.log(1)
  }, 1000)
  console.log(2)
})
console.log(3)

则执行顺序为 2 3 1

let p1 = new Promise((resolve, reject) => {
  console.log(1)
  resolve('了解')
  console.log(2)
})
p1.then(result => {
  console.log('成功' + result)
}, reason => {
  console.log('失败' + result)
})
console.log(3)

则上述执行顺序应为 1 2 3 成功了解 解析: 开始先执行第一遍的所有同步代码,1 2 遇到then的时候是微任务所以继续往下进行。所以输出3 。3执行完后,去清空微任务队列。打印 “成功了解”

例:

new Promise(resolve => {
  resolve(a)
  // 报错
}).then(result => {
  console.log(`成功: ${result}`)
  return result * 10
},reason => {
  console.log(`失败: ${reason}`)
  // 执行这句时没有返回异常或者返回一个失败的Promise实例,会继续执行下个then
}).then(result => {
  console.log(`成功:${result}`)
}, reason => {
  console.log(`失败: ${reason}`)
})

输出顺序: 失败:referenceError a is not defined 成功 undefined

4 then中含有return的情况

当then的回调函数中存在return值的时候:

.then(() => return 2)
.then(result => console.log(result)) // 2

.then(() => return new Promise())
.then(result => console.log(result)) // 上面的 new Promise

所有这里的return 和 resolve差不多。当then中没有return也会返回一个Promise新实例供下一个then使用。

5 Promise常用的方法

5.1 Promise.resolve()

Promise.resolve('foo')就等价于 new Promise(resolve => resolve('foo'))

Promise.resolve的参数有以下4中情况

1.参数为一个Promise实例

如果参数是Promise实例,则Promise.resolve将原封不动的返回该实例。

const p1 = new Promise(function (resolve, reject){
  setTimeout(() => reject(new Error('fail')), 3000)
})

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

p2.then(result => console.log(result)).catch(err => console.log(err))

p1 3秒后变为reject, p2 1秒后就变为 P1, 所以P2.then针对的是P1的状态变化。最后应为err 触发catch

2.参数不是具有then的对象,或者根本不是对象

Promise.resolve('success').then(value => {
  console.log(value) // success
}, function(value) {

})

参数会传给then回调函数

3.不带任何参数

Promise.resolve方法调用时,如果不带参数,则直接返回一个resolve状态的Promise对象

如果希望获得一个Promise对象,则可以直接调用Promise.resovle()

Promise.resolve().then(() => console.log('two'))

4.参数是thenable对象

thenable对象值得是具有then方法的对象, Promise.resolve方法会将这个对象转为Promise对象,然后立即执行thenable对象的then方法。

let thenable = {
  thenfunction(resolve, reject) {
    resolve()
  }
}
let P1 = Promise.resolve(thenable)

P1.then(function(value) {console.log(value)})  // 42

5.2 Promise.reject

返回一个带有拒绝原因的Promise对象

new Promise((resolve, reject) => reject(new Error('出错了')))

// 等价于

Promise.reject(new Error('出错了'))

5.3 Promise.all

生成并返回一个新的Promise对象,它可以使用Promise实例的所有方法。

参数为Promise对象组成的数组,等所有的Promise对象都变成resolve的时候,该方法才返回。

let p1 = Promise.resolve(1)
let p2 = new Promise(resolve => {
  setTimeout(() => {
    resolve(2)
  }, 1000)
})
let p3 = Promise.resolve(3)

Promise.all([p3, p2, p1]).then(result => {
  console.log(result) // [3, 2, 1] 按照顺序返回
})

注意: 参数中任一个Promise为reject,则Promise.all停止

扩展:项目中调用接口时,可以自己套一层Promise

return new Promise((resolve, reject) => {
  this.$service.network.get(url).then(res => {
    resolve(res)
  })
})

5.4 Promise.allSettled

与Promise.all类似,唯一不同点是不会短路。纵使某一个Promise对象reject,但不影响进程,最后我们可以筛选出成功的Promise(filter)

5.5 Promise.race

Promise.race与all的用法一样,接收一个Promise对象数组为参数

区别:

all是所有对象都变为FulFilled或者rejected状态,才进行then回调函数

race是只有存在一个Promise的状态变为FulFilled 或者 rejected就进行then函数回调。

5.6 Promise.prototype.finally

ES9新增finally方法返回Promise,在Promise结束时,无论结果是fulfilled或者是rejected。都会执行的回调函数。

这为Promise无论成功与否都需要执行的代码提供了一种方式,避免了then和catch中各写一次的情况。

this.loading = true
request().then(res => {
  console.log(res)
})
.catch(err => {
   console.log(err)
 })
.finally(() => {
   this.loading = false
})

只有Promise状态发生变化后,才会执行finally。且finally方法的回调函数不接受任何参数,与状态无关。

new Promise(() => {

}).finally(() => {
  console.log(111)
})
// 此时的状态一直是pending状态,所有finally不会执行的

5.7 Promise.any

最先一个resolve出来的,只有当所有的Promise对象都rejected。Promise.any才会输出reject。注意与Promise.race的区别