Promise的使用

100 阅读9分钟

学习了coderwhy的JavaScript高级语法视频课的笔记

如有错误或者不合适的地方,敬请见谅,欢迎指出和拓展,谢谢各位了

1. 异步请求的处理-方式一(切入点)

// 模拟网络的异步请求
function requestData(url, successCallback, failureCallback) {
  setTimeout(() => {
    if (url === 'aaa') {
      const names = ['a', 'b', 'c']
      successCallback(names)
    } else {
      const errMessage = '请求失败,url地址不正确'
      failureCallback(errMessage)
    }
  }, 1000)
}

requestData(
  'aaa',
  res => {
    console.log(res)
  },
  err => {
    console.log(err)
  }
)

/**
 * 这种回调的方式有很多的弊端:
 *  1> 如果是我们自己封装的requestData,那么我们在封装的时候必须要自己设计好callback名称, 并且使用好
 *  2> 如果我们使用的是别人封装的requestData或者一些第三方库, 
       那么我们必须去看别人的源码或者文档, 才知道它这个函数需要怎么去获取到结果
 */

2. Promise的基本使用

  • 在通过new创建Promise对象时,我们需要传入一个回调函数,我们称之为executor:
    • 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject;
    • 当我们调用resolve回调函数时,会执行Promise对象的then方法传入的第一个回调函数;
    • 当我们调用reject回调函数时,会执行Promise对象的then方法传入的第二个回调函数,或catch方法传入的回调函数(catch方法后面再讲)。
const promise = new Promise((resolve, reject) => {
  resolve('成功')
  //   reject('失败')
})

promise.then(
  res => {
    console.log(res)
  },
  err => {
    console.log(err)
  }
)

上面Promise使用过程,我们可以将它划分成三个状态:

  • 待定(pending): 初始状态,既没有被兑现,也没有被拒绝;
    • 当执行executor中的代码时,处于该状态;
  • 已兑现(fulfilled): 意味着操作成功完成,我们也可以称之为已决议(resolved);
    • 执行了resolve时,处于该状态;
  • 已拒绝(rejected): 意味着操作失败;
    • 执行了reject时,处于该状态。

注意: Promise状态一旦确定下来, 那么就是不可更改的(锁定)

3. 异步请求的处理-方式二(Promise)

// 模拟网络的异步请求
function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === 'aaa') {
        const names = ['a', 'b', 'c']
        resolve(names)
      } else {
        const errMessage = '请求失败,url地址不正确'
        reject(errMessage)
      }
    }, 1000)
  })
}

const promise = requestData('aaa')

promise.then(
  res => {
    console.log(res)
  },
  err => {
    console.log(err)
  }
)

4. Promise的resolve的参数

/**
 * resolve(参数)
 *  1、普通的值或者对象  pending -> fulfilled
 *  2、传入一个Promise
 *    那么当前的Promise的状态会由传入的Promise来决定
 *    相当于状态进行了移交
 *  3、传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口)
 *    那么也会执行该then方法, 并且又该then方法决定后续状态
 */

// 1、普通的值或者对象  pending -> fulfilled
const promise = new Promise((resolve, reject) => {
  resolve(111)
})

promise.then(
  res => {
    console.log(res) //111
  },
  err => {}
)

// 2、传入一个Promise,那么当前的Promise的状态会由传入的Promise来决定,相当于状态进行了移交
const promiseResolve = new Promise((resolve, reject) => {
  reject(222)
})

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

promise2.then(
  res => {},
  err => {
    console.log(err) //222
  }
)

// 3、传入一个对象, 并且这个对象有实现then方法(并且这个对象是实现了thenable接口),
// 那么也会执行该then方法, 并且又该then方法决定后续状态
const obj = {
  then: function (resolve, reject) {
    reject(333)
  }
}

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

promise3.then(
  res => {},
  err => {
    console.log(err) //333
  }
)

5. Promise的对象方法

(1). then方法

  • 事实上then方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法
// 1.同一个Promise可以被多次调用then方法
// 当我们的resolve方法被回调时, 所有的then方法传入的回调函数都会被调用
const promise = new Promise((resolve, reject) => {
  resolve('xxxx')
})

promise.then(res => {
  console.log(res) //xxx
})

promise.then(res => {
  console.log(res) //xxx
})

// 2.then方法传入的 "回调函数: 可以有返回值,默认return是undefined
// then方法本身也是有返回值的, 它的返回值是Promise

// (1)如果我们返回的是一个普通值(数值/字符串/普通对象/undefined),
// 那么这个普通的值被作为一个新的Promise的resolve值
const promise21 = new Promise((resolve, reject) => {
  resolve('xxxx')
})

promise21
  .then(res => {
    return 111
  })
  .then(res => {
    console.log(res) //111
  })

// (2)如果我们返回的是一个Promise
const promise22 = new Promise((resolve, reject) => {
  resolve('xxx')
})

promise22
  .then(res => {
    return new Promise((resolve, reject) => {
      resolve(222)
    })
  })
  .then(res => {
    console.log(res) //222
  })

// (3)如果返回的是一个对象, 并且该对象实现了thenable
const promise23 = new Promise((resolve, reject) => {
  resolve('xxx')
})

promise23
  .then(res => {
    return {
      then: function (resolve, reject) {
        resolve(333)
      }
    }
  })
  .then(res => {
    console.log(res) //333
  })

(2). catch方法

  • 事实上catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法
// 1.当executor抛出异常时, 也是会调用错误(拒绝)捕获的回调函数的
const promise = new Promise((resolve, reject) => {
  throw new Error('错误')
})

promise.then(undefined, err => {
  console.log(err)
})

// 2.通过catch方法来传入错误(拒绝)捕获的回调函数
const promise2 = new Promise((resolve, reject) => {
  throw new Error('错误2')
  // resolve(222)
})

promise2.catch(err => {
  console.log(err)
})

promise2
  .then(res => {
    throw new Error('错误22')
  })
  .catch(err => {
    //throw new Error('错误2'),如果resolve(222)则是throw new Error('错误22')
    console.log(err)
  })

// 3.拒绝捕获的问题
const promise3 = new Promise((resolve, reject) => {
  // resolve(333)
  reject('错误3')
})

// 这里then方法和catch方法是两次独立的调用,所以then方法中没有捕获异常的方法而报错
promise3.then(res => {
  console.log(res)
})

promise3.catch(err => {
  console.log(err)
})

// 4.catch方法的返回值
//事实上catch方法也是会返回一个Promise对象的,所以catch方法后面我们可以继续调用then方法或者catch方法
const promise4 = new Promise((resolve, reject) => {
  reject(111)
})

promise4
  .then(res => {})
  .catch(err => {
    // return 222
    return new Promise((resolve, reject) => {
      resolve(222)
      // reject(222)
    })
  })
  .then(
    res => {
      console.log(res)
    },
    err => {
      console.log(err)
    }
  )

promise4
  .then(
    res => {},
    err => {
      // return 222333
      return new Promise((resolve, reject) => {
        resolve(222333)
        // reject(222333)
      })
    }
  )
  .then(
    res => {
      console.log(res)
    },
    err => {
      console.log(err)
    }
  )

(3). finally方法

  • finally是在ES9(ES2018)中新增的一个特性:表示无论Promise对象无论变成fulfilled还是reject状态,最终都会被执行的代码
  • finally方法是不接收参数的,因为无论前面是fulfilled状态,还是reject状态,它都会执行。
const promise = new Promise((resolve, reject) => {
  reject(111)
})

promise
  .then(res => {
    console.log(res)
  })
  .catch(err => {
    console.log(err)
  })
  .finally(() => {
    console.log('finally')
  })

6. Promise的类方法

  • 前面我们学习的then、catch、finally方法都属于Promise的实例方法,都是存放在Promise的prototype上的。

(1). resolve类方法

  • 有时候我们已经有一个现成的内容了,希望将其转成Promise来使用,这个时候我们可以使用 Promise.resolve方法来完成。
  • resolve参数的形态:
    • 情况一:参数是一个普通的值或者对象;
    • 情况二:参数本身是Promise;
    • 情况三:参数是一个thenable —— {then:functio(resolve,reject){...}}。
//转成promise对象
// 1、
function foo() {
  const obj = {
    name: 'xxx',
    age: 18
  }

  return new Promise((resolve, reject) => {
    resolve(obj)
  })
}

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

// 2、
const promise2 = Promise.resolve({
  name: 'xxx',
  age: 18
})
//相当于
// const promise2 = new Promise((resolve, reject) => {
//   resolve(obj)
// })

promise2.then(res => {
  console.log(res)
})

(2). reject类方法

  • reject方法类似于resolve方法,只是会将Promise对象的状态设置为reject状态。
  • 注意: Promise.reject传入的参数无论是什么形态,都会直接作为reject状态的参数传递到catch的,无论传入什么值都是一样的
const promise = Promise.reject(
  new Promise((resolve, reject) => {
    resolve('bbb')
  })
)
//相当于
// const promise = new Promise((resolve, reject) => {
//   reject(
//     new Promise((resolve, reject) => {
//       resolve('bbb')
//     })
//   )
// })

promise
  .then(res => {})
  .catch(err => {
    console.log('err:' + err) //err:[object Promise]
  })

(3). all类方法

  • 它的作用是将多个Promise包裹在一起形成一个新的Promise;
  • 新的Promise状态由包裹的所有Promise共同决定:
    • 当所有的Promise状态都变成fulfilled状态时,新的Promise状态为fulfilled,并且会将所有Promise的返回值 组成一个数组;
    • 当有一个Promise状态为reject时,新的Promise状态为reject,并且会将第一个reject的返回值作为参数
// 1、
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 1000)
})

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

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

Promise.all([p1, p2, p3, '444'])
  .then(res => {
    console.log(res) //[ 111, 222, 222, '444' ]
  })
  .catch(err => {
    console.log(err)
  })
  
// 2、
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 1000)
})

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

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

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

(4). allSettled类方法

  • all方法有一个缺陷,当有其中一个Promise变成reject状态时,新Promise就会立即变成对应的reject状态,那么对于resolved的,以及依然处于pending状态的Promise,我们是获取不到对应的结果的。
  • 在ES11(ES2020)中,添加了新的API Promise.allSettled:
    • 该方法会在所有的Promise都有结果(settled),无论是fulfilled,还是reject时,才会有最终的状态;
    • 并且这个Promise的结果一定是fulfilled的
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 1000)
})

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

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

Promise.allSettled([p1, p2, p3, '444'])
  .then(res => {
    console.log(res)
    //   [
    //     { status: 'fulfilled', value: 111 },
    //     { status: 'rejected', reason: 222 },
    //     { status: 'fulfilled', value: 222 },
    //     { status: 'fulfilled', value: '444' }
    //   ]
  })
  .catch(err => {
    console.log(err)
  })

(5). race类方法

  • 如果有一个Promise有了结果,我们就希望决定最终新Promise的状态,那么可以使用race方法:
    • race是竞技、竞赛的意思,表示多个Promise相互竞争,谁先有结果,那么就使用谁的结果。
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(111)
  }, 2000)
})

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

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

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

(6). any类方法

  • any方法是ES12中新增的方法,和race方法是类似的:
    • any方法会等到一个fulfilled状态,才会决定新Promise的状态;
    • 如果所有的Promise都是reject的,那么也会等到所有的Promise都变成rejected状态,然后报一个AggregateError的错误。
// 1、
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(111)
  }, 1000)
})

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

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

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

// 2、
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(111)
  }, 1000)
})

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

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

Promise.any([p1, p2, p3])
  .then(res => {
    console.log(res) //222
  })
  .catch(err => {
    console.log('err:' + err) //err:AggregateError: All promises were rejected
  })