Promise小结

简介

Promise 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果

从语法上说,Promise 是一个对象,从它可以获取异步操作的消息

Promise对象状态:

  • Pending:进行中
  • Resolved:又称Fulfilled,已完成
  • Rejected:已失败
  • 状态的改变只有两种可能:从Pending变为Resolved、从Pending变为Rejected

Promise对象特点:

  1. 对象的状态不受外界影响:只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果
  3. 无法取消Promise,一旦新建它就会立即执行,无法中途取消
  4. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部
  5. 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

基本用法

// 创建Promise示例
var promise = new Promise((resolve, reject) => {
  // ...
  if (/* 异步操作成功 */) {
    resolve(value)
  } else {
    reject(error)
  }
})

// 指定Resolved状态和Reject状态的回调函数
promise.then(value => {
  // success
}, error => {
  // failure
})
  • resolve函数作用:将Promise对象的状态从Pending变成Resolved。在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
  • reject函数作用:将Promise对象的状态从Pending变成Rejected。在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去
  • then方法接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数(可选,不一定要提供)是Promise对象的状态变为Reject时调用
// 异步加载图片
function loadImageAsync(url) {
  return new Promise((resolve, reject) => {
    var image = new Image()
    image.onload = () => {
      resolve(image)
    }
    image.onerror = () => {
      reject(new Error('Could not load image at' + url))
    }
    image.src = url
  })
}

// Ajax操作
function getJson(url) {
  return new Promise((resolve, reject) => {
    var client = new XMLHttpRequest()
    client.open('GET', url)
    client.onreadystatechange = handler
    client.responseType = 'json'
    client.setRequestHeader('Accept', 'application/json')
    client.send()

    function handler() {
      if (this.readState !== 4) {
        return
      }
      if (this.status === 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
  })
}
getJson('posts.json').then(json => {
  console.log('Contents: ' + json)
}, error => {
  console.error('出错了', error)
})

Promise.prototype.then()

then方法是定义在原型对象Promise.prototype上的

Promise实例添加状态改变时的回调函数

第一个参数是Resolved状态的回调函数

第二个参数(可选)是Rejected状态的回调函数

then方法返回的是一个新的Promise实例

采用链式的then,可以指定一组按照次序调用的回调函数

如果前一个回调函数返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化

getJson('post/1.json')
  .then(post => getJson(post.commentUrl))
  .then(
    comments => console.log('Resolved: ', comments),
    err => console.error('Rejected: ', err)
  )

Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数

Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法

catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法

getJson('/posts.json').then(posts => {
  // ...
}).catch(error => {
  // 处理 getJson 和 前一个回调函数运行时发生的错误
  console.error('发生错误!', error)
})

// bad
promise.then(data => {
  // success
}, err => {
  // error
})

// good
// 可以捕获前面 then 方法执行中的错误
// 也更接近同步的写法(try/catch)
// 建议总是使用 catch 方法,而不使用 then 方法的第二个参数
promise.then(data => {
  // success
}).catch(err => {
  // error
})

Promise.prototype.finally()

finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

该方法是 ES2018 引入标准的

promise
  .then(res => console.log(res))
  .catch(err => console.error(err))
  .finally(() => {
    console.log('finally的执行与状态无关')
  })

Promise.resolve()

将现有对象转为Promise对象

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

参数是一个Promise实例

Promise.resolve将不做任何修改、原封不动地返回这个实例

参数是一个thenable对象

Promise.resolve方法会将这个对象转为Promise对象,然后就立即执行thenable对象的then方法

let thenable = {
  then: function(resolve, reject) {
    resolve(42)
  }
}

let p1 = Promise.resolve(thenable)
p1.then(function(value) {
  console.log(value)  // 42
})
// thenable 对象的 then 方法执行后,对象 p1 的状态就变为 resolved
// 从而立即执行最后那个 then 方法指定的回调函数,输出42

参数不是具有then方法的对象,或根本就不是对象

参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的Promise对象,状态为Resolved

var p = Promise.resolve('Hello')
p.then(function (s){
  console.log(s)  // Hello
})
// 由于字符串 Hello 不属于异步操作(判断方法是它不是具有then方法的对象)
// 返回 Promise 实例的状态从一生成就是 Resolved
// 所以回调函数会立即执行

不带有任何参数

直接返回一个Resolved状态的Promise对象

Promise.reject()

返回一个新的 Promise 实例,该实例的状态为rejected

var p = Promise.reject('出错了')
// 等同于
var p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, s => {
  console.log(s)  // 出错了
})
// 生成一个 Promise 对象的实例 p,状态为 rejected
// 回调函数会立即执行

Promise.all()

Promise.all(iterable) 方法返回一个 Promise 实例

iterable 参数是一个可迭代的对象,如 Array 或者 String

iterable 参数内所有的 promiseresolved 或参数中不包含 promise 时回调完成(resolve

Promise.all 返回的 promise 的完成状态的结果是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)

如果参数中 promise 有一个rejected,此实例回调失败(reject)。而不管其它 promise 是否完成

失败原因的是第一个失败 promise 的结果

// Promise.all()的一个简版实现。模拟实现
function all(iterable) {
  return new Promise((resolve, reject) => {
    let index = 0
    let elementCount = 0
    let anErrorOccurred = false
    const result = new Array(iterable.length)
    for (const promise of iterable) {
      // Capture the current value of 'index'
      const currentIndex = index;
      promise.then(value => {
        if (anErrorOccurred) return
        result[currentIndex] = value
        elementCount++
        if (elementCount == result.length) {
          resolve(result)
        }
      }, err => {
        if (anErrorOccurred) return
        anErrorOccurred = true
        reject(err)
      })
      index++
    }
    if (index === 0) {
      resolve([])
      return
    }
  })
}
// 使用示例
Promise.all([getJson('/posts.json'), getJson('/post/1.json')]).then(values => {
  // posts.json 和 post/1.json 都请求回来后,进入到这里
  // 请求回来的数据存储在 values 里面,顺序跟请求的一样 [ posts.json的结果, post/1.json的结果 ]
  console.log(values)
}).catch(err => {
  console.error(err)
})

Promise.race()

Promise.race(iterable) 方法返回一个 promise

一旦迭代器中的某个 promise 解决或拒绝,返回的 promise 就会解决或拒绝

// Promise.race() 的一个简版实现。模拟实现
function race(iterable) {
  return new Promise((resolve, reject) => {
    let settlementOccurred = false
    for (const promise of iterable) {
      promise.then(value => {
        if (settlementOccurred) return
        settlementOccurred = true
        resolve(value)
      }, err => {
        if (settlementOccurred) return
        settlementOccurred = true
        reject(err)
      })
    }
  })
}
// 使用示例
Promise.race([getJson('/posts.json'), getJson('/post/1.json')]).then(value => {
  // posts.json 和 post/1.json 只要有一个返回了,就进入到这里
  // 请求回来的数据存储在 value 里面,是首先返回的那个数据
  // 有可能是 posts.json的结果,也有可能是 post/1.json的结果
  console.log(value)
}).catch(err => {
  // 在没有返回的时候 先报错了
  console.error(err)
})

Promise.allSettled()

Promise.allSettled() 方法返回一个promise

promise在所有给定的promise已被解析或被拒绝后解析

并且每个对象都描述每个promise的结果

该方法是 ES2020 引入标准的。使用的时候需要在代码中先查看是否已有

// Promise.allSettled() 的简化实现。模拟实现
function allSettled(iterable) {
  return new Promise((resolve, reject) => {
    function addElementToResult(i, elem) {
      result[i] = elem
      elementCount++
      if (elementCount === result.length) {
        resolve(result)
      }
    }

    let index = 0
    let elementCount = 0
    const result = new Array(iterable.length)
    for(const promise of iterable) {
      // Capture the current value of 'index'
      const currentIndex = index
      promise.then(value => {
        addElementToResult(currentIndex, {
          status: 'fulfilled',
          value
        })
      }, reason => {
        addElementToResult(currentIndex, {
          status: 'rejected',
          reason
        })
      })
      index++
    }
    if (index === 0) {
      resolve([])
      return
    }
  })
}
// 使用示例
// 首先需要实现一个allSettled
if (typeof Promise.allSettled !== 'function') {
  Promise.allSettled = function(promises) {
    return new Promise((resolve, reject) => {
      if (!Array.isArray(promises)) {
        return reject(new TypeError('arguments must be an array'))
      }
      let counter = 0
      const len = promises.length
      const result = new Array(len)
      promises.forEach((promise, index) => {
        promise.then(value => {
          addElementToResult(index, {
            status: 'fulfilled',
            value
          })
        }, reason => {
          addElementToResult(index, {
            status: 'rejected',
            reason
          })
        })
      })

      function addElementToResult(i, elem) {
        result[i] = elem
        counter++
        if (counter === len) {
          resolve(result)
        }
      }
    })
  }
}

Promise.allSettled([getJson('/posts.json'), getJson('/post/1.json')]).then(values => {
  // posts.json 和 post/1.json 都请求回来后,进入到这里
  // 请求回来的数据存储在 values 里面,顺序跟请求的一样 [ posts.json的结果, post/1.json的结果 ]
  console.log(values)
})

示例

const func1 = () => {
  return new Promise((resolve, reject) => {
    // reject('this is error')
    // try {
      aa
      resolve(1)
    // } catch(err) {
    //   console.log(err)
    //   reject(err)
    // }
  }).catch(err => {
    // aa会报语法性错误,这里可以捕捉到;
    // 捕捉到错误后,通过 return 往后传
    console.log(1, err)
    return Promise.reject(err)
  })
}

// 这里会打印出来错误信息 ReferenceError: aa is not defined
func1().then(res => {
  console.log(res)
}).catch(err => {
  console.log(err)
})


const func2 = () => {
  return new Promise(async (resolve, reject) => {
    // 使用 try catch 的方式,捕捉promise的reject,然后往后传
    try {
      const res = await func1()
      resolve(res + ' --- 2')
    } catch(err) {
      console.log(err, '----')
      reject(err)
    }
  })
  // .catch(err => {
  //   // 这里的 catch 是捕捉不到错误的
  //   // await func1() 返回的是reject,只是不执行后面的语句而已,不会报错
  //   // 如果想捕捉错误往后传,可以像上面几行代码那样,使用 try catch 捕捉
  //   console.log(2, err)
  //   return Promise.reject(err)
  // })
}

func2().then(res => {
  console.log(res)
}).catch(err => {
  console.log('执行-', err)
})

创作不易,若本文对你有帮助,欢迎打赏支持作者!

 分享给好友: