异步

允许程序在等待某些操作完成时继续执行其他任务,而不是阻塞或等待操作完成。
通俗讲就是一个任务不是连续完成的,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段;相应地,连续的执行就叫做同步。

任务执行后不会立即返回执行的结果,现在开始执行的行为,等待一段时间后完成
可以一起执行多个任务,如果任务 A 需要等待,可先执行任务 B,等到任务 A 结果返回后再继续回调
异步任务会在当前脚本的所有同步任务执行完才会执行

回调

可以简单理解为:(任务执行完)回(来)调(用)的函数

同步回调

function A(callback) {
  console.log('执行主函数的回调函数前')
  callback()
  console.log('执行主函数的回调函数后')
}
function B() {
  console.log('执行回调函数')
}
A(B)
// 结果:
// 执行主函数的回调函数前
// 执行回调函数
// 执行主函数的回调函数后

异步回调

function A(callback) {
  console.log('执行主函数的回调函数前')
  callback()
  console.log('执行主函数的回调函数后')
}
function B() {
  setTimeout(function() {
      console.log('执行异步回调函数')
  }, 0)
  console.log('执行同步回调函数')
}
A(B)
// 结果:
// 执行主函数的回调函数前
// 执行同步回调函数
// 执行主函数的回调函数后
// 执行异步回调函数

缺点

回调地狱:有一个异步获取数据的操作需要写一个回调函数,如果这个操作里又触发了另一个获取数据的操作,那么就需要在回调里嵌套回调,降低了代码的可读性,代码也越来越难管理

PromiseES6

  • Promise 是一个构造函数,用来生成 Promise 实例
  • Promise 接受一个【函数】作为参数,这个函数同时也包含两个函数参数(resolve,reject),这两个函数是回调函数,由 JavaScript 引擎提供
  • Promise 构造函数里的 return 值会被忽略,也不会改变 Promise 的状态
const myPromise = new Promise((resolve, reject) => {
  // 做一些异步操作,最终需要手动调用下面两者之一:
  //
  //   resolve(someValue) // fulfilled
  // 或
  //   reject("failure reason") // rejected
})

Tip

new 一个 Promise 时,不需要调用它就会立即执行传入的函数,无法取消。如果不想立即执行,可以把它包在一个函数中,使用时调用那个函数即可

状态

Promise 的初始状态是 pending

  • 调用 resolve(value) 会把 Promise 的状态置为 fulfilled
  • 调用 reject(error) 会把 Promise 的状态置为 rejected

Note

  • 一个 resolved 或 rejected 的 promise 都会被称为 settled
  • resolve 或 reject 都只接受最多一个参数,其余的参数被忽略
  • 只能调用一个 resolve 或一个 reject,其他的都会被忽略,任何状态的更改都是单向的,不会再变更
  • resolve 或 reject 不会中断后面的代码,会继续执行,可以加 return 跳过后面的执行

700

then、catch、finally

当状态发生变化,后续的处理函数会执行的操作

then

promise.then(
  function onFulfilled() {},
  function onRejected() {}
)
  • 它可以接受两个函数参数,第一个对应 resolve(),第二个对应 reject()
  • then 方法也可以返回一个包含 then 方法的对象(thenable),它也会被当做一个 promise 来对待
  • return 的值会由 Promise.resolve(return的返回值); 进行包装处理,因此不管回调函数中会返回一个什么样的值,最终 then 的结果都是返回一个新创建的 promise 对象

catch

promise.catch(
  onRejected() {}
)
  • 该方法等同于 .then(null, onRejected),它只是一个简写形式
  • 但是在执行 then 回调时如果遇到异常(代码出错),或者手动抛出异常(throw),会进入到 .catch() 里面,而不会进入到 then 的第二个回调里

then catch 的返回值

  • 如果 then、catch 中没有对应状态的回调函数(或参数不是函数类型),那就会返回一个 与调用该方法的 Promise 相同的 新 Promise 对象(也就是向后传递)
  • 如果手动返回一个 Promise,则返回 状态、值与之相同的新 Promise,其状态变更时,这个新的 Promise 也会变更
  • 不管怎么样,返回的都是一个新的 Promise 实例,因此可以采用链式写法

finallyES2018

promise.finally(
  () => {}
)
  • 状态确定(settled)后执行,不管 Promise 对象最后状态如何,都会执行的操作,一般用于执行一些清理工作
  • finally 函数没有参数,也不需要返回值(会被忽略)。它返回与调用该方法的 Promise 对象 相同的 新 Promise 对象
  • finally 里抛出 error 时,执行将转到最近的 error 的处理程序

Promise 链

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000)
}).then(function(result) {
  console.log(result) // 1
  return result * 2
}).then(function(result) {
  console.log(result) // 2
  return result * 2
}).then(function(result) {
  console.log(result) // 4
  return result * 2
})
 
// 1 2 4

注意:多个 .then 添加到 Promise 上并不是 Promise 链,只是互不影响的并行关系:

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000)
})
 
promise.then(function(result) {
  console.log(result) // 1
  return result * 2
})
 
promise.then(function(result) {
  console.log(result) // 1
  return result * 2
})
 
promise.then(function(result) {
  console.log(result) // 1
  return result * 2
})
 
// 1 1 1

错误处理

.catch 不仅捕获 reject() 抛出的错误,还会捕获 promise 中产生的语法错误或其他错误(promise 的处理程序周围有一个“隐式的 try..catch”,如果发生异常,它就会被捕获,并被视为 rejection 进行处理)

异步错误:

new Promise(function(resolve, reject) {
  setTimeout(() => {
    throw new Error("Whoops!")
  }, 1000)
}).catch(alert)

promise 的 catch 不能捕获异步产生的错误,这里的错误并不是在 executor 运行时生成的,而是在稍后生成的。因此 promise 无法处理它。需要改成:

new Promise(function(resolve, reject) {
  setTimeout(() => {
    reject(new Error("Whoops!"))
  }, 1000)
}).catch(alert)

Note

Promise 内部的错误不会影响到 Promise 外部的代码,也不会终止代码继续执行

主要用途

有异步操作时,使用 Promise 对这个异步操作进行封装(promise 可以按顺序进行编码,链式调用,更加具有可读性和灵活性)

缺点

  • 无法取消 Promise,一旦新建它就会立即执行
  • 当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
  • 代码冗余,一眼看去一堆 then,原来的语义变得很不清楚

常用方法

  • Promise.all( iterable ):并行执行多个 promise,全部 fulfilled 则 fulfilled;任意一个 rejected 立即 rejected
  • Promise.allSettled( iterable ):所有的 promise 都 settled 则 fulfilledES2020
  • Promise.race( iterable ):只等待第一个 settled 的 promise 并获取其结果
  • Promise.any( iterable ):只等待第一个 fulfilled 的 promise 并获取其结果ES2021

很少使用 或 被 async 替代:

  • Promise.resolve( value ):用结果 value 创建一个 resolved 的 promise
  • Promise.reject( error ):用 error 创建一个 rejected 的 promise

Promise.resolve(1) 可以认为是以下代码的语法糖:

new Promise(function(resolve) {
  resolve(1)
})

Promise.reject(new Error(“出错了”)) 可以认为是以下代码的语法糖:

new Promise(function(resolve, reject) {
    reject(new Error("出错了"))
})

用了几年的Promise,竟然还搞不清楚返回值是什么~ - 掘金 (juejin.cn)