ES6-Promise类

122 阅读6分钟

前言

Promise是异步编程的一种解决方案,可以解决回调地狱。

优点:

  1. 可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
  2. 提供统一的接口,使得控制异步操作更加容易。不需要再自己封装,同时提高代码阅读的效率。

缺点:

  1. 无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  2. 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  3. 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

一、基本使用

1.立即执行

在通过new创建Promise对象时,需要传入一个回调函数,称之为executor

  • 这个回调函数会立即被执行,并且给传入另外两个回调函数resolvereject
  • 当调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数;
  • 当调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数。
const promise = new Promise(() => {
  console.log('被立即执行');
})
// 被立即执行

2.then/catch

then方法传入的两个回调函数:第一个回调函数会在Promise执行resolve时回调,第二个回调函数会在Promise执行reject时回调。

// 4.then方法有两个参数
function foo () {
  return new Promise((resolve, reject) => {
    // resolve(200)
    reject(404)
  })
}
const promise = foo()
promise.then((res) => {
  console.log(res);
}, (err) => {
  console.log(err);
})
// 等价于
// promise.then((res) => {
//   console.log(res);
// }).catch((err) => {
//   console.log(err);
// })

二、三种状态

状态一旦确定下来,就不可更改(锁定)。

  1. 待定(pending):初始状态,既没有被兑现,也没有被拒绝;
    • 当执行executor中的代码时,处于该状态;
  2. 已兑现(fulfilled):意味着操作成功完成;
    • 执行了resolve时,处于该状态;
  3. 已拒绝(rejected):意味着操作失败。
    • 执行了reject时,处于该状态。

所以在executor中先resolvereject或者先rejectresolve,后面的处理都是无效的。

new Promise((resolve, reject) => {
  resolve(200)
  // 以下代码无效
  reject(404)
}).then((res) => {
  console.log(res, 'then---');
}).catch((err) => {
  console.log(err, 'err---');
})
// 200

三、then方法

  1. 同一个Promise可以被多次调用then方法。当resolve方法被回调时,所有的then方法传入的回调函数都会被调用。
function foo () {
  return new Promise((resolve, reject) => {
    resolve(200)
  })
}
const promise = foo()
promise.then((res) => {
  console.log(res);
})
promise.then((res) => {
  console.log(res);
})
promise.then((res) => {
  console.log(res);
})
// 200
// 200
// 200
  1. then方法传入的“回调函数”,可以有返回值。 then方法本身有返回值,是一个Promise
  • 如果返回的是普通值(数字/字符串/普通对象/undefined),那么普通值被作为一个新的Promiseresolve值。

Promise中then的链式调用原理:前一个then的返回值返回给后一个then

function foo () {
  return new Promise((resolve, reject) => {
    resolve(200)
  })
}
const promise = foo()
const result = promise.then((res) => {
  return 'aa'
})
// 等价于
const result1 = promise.then((res) => {
  return new Promise((resolve, reject) => {
    resolve('aa')
  })
})
console.log(result === result)
// true
  • 如果返回的是一个Promisethen/catch之后的状态取决于返回的Promise的状态。
function foo () {
  return new Promise((resolve, reject) => {
    resolve(200)
  })
}
const promise = foo()
promise.then((res) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(202)
    }, 3000)
  })
}).then((res) => {
  console.log(res);
})
// 202
  • 如果返回的是一个对象,并且该对象实现了thenable
function foo () {
  return new Promise((resolve, reject) => {
    resolve(200)
  })
}
const promise = foo()
promise.then((res) => {
  // 这里的return相当于new Promise(resolve => resolve(obj.then))
  // 所以将obj中then函数内的状态返回给下一个then
  return {
    then: function (resolve, reject) {
      resolve(204)
    }
  }
}).then((res) => {
  console.log(res);
})
// 204

三、catch方法

  1. 可以捕获的异常:
  • reject()
  • throw new Error()
function foo () {
  return new Promise((resolve, reject) => {
    resolve(200)
  })
}
const promise = foo()
promise.then((res) => {
  return new Promise((resolve, reject) => {
    // reject(404)
    throw new Error(405)
  })
}).catch((err) => {
  console.log(err);
})
// Error: 405
// ......

Promise中catch的链式调用原理:依次从头捕获调用链的异常,不一定是上一个then的异常。

const promise = new Promise((resolve, reject) => {
  // 异常1
  // reject(404)
  resolve(200)
})
promise.then((res) => {

}).then((res) => {
  // 异常2
  throw new Error(405)
}).catch((err) => {
  console.log(err);
})
// 405
  1. catch方法也有返回值,是一个Promise
const promise = new Promise((resolve, reject) => {
  reject(404)
})
promise.then((res) => {

}).catch((err) => {
  // return '11'
  // 等价于
  return new Promise((resolve, reject) => {
    resolve('11')
  })
}).then((res) => {
  console.log(res, 'res---then2');
}).catch((err) => {
  console.log(err, 'err---catch2');
})
// 11 res---then2

四、finally方法

  • 无论Promise对象变成fulfilled还是rejected状态,最终都会执行finally代码。
  • 不接收参数。所以无法得知前一步是fulfilled还是rejected状态。
const promise = new Promise((resolve, reject) => {
  reject(404)
})
promise.then((res) => {
  return '200'
}).catch((err) => {
  return err
}).finally((res) => {
  console.log(res, 'finally');
})
// undefined finally

五、类方法-resolve/reject

  1. Promise.resolve()then方法传入不同类型的值现象一致。
// const promise = Promise.resolve({ name: 'resolve' })
// 等价于
const promise = new Promise((resolve, reject) => {
  resolve({name: 'resolve'})
})
promise.then((res) => {
  console.log(res);
})
// { name: 'resolve' }
  1. Promise.reject()

传入什么值就返回什么值。

const promise = Promise.reject({ name: 'reject' })
// 等价于
// const promise = new Promise((resolve, reject) => {
//   reject({name: 'reject'})
// })
promise.then((res) => {
}).catch(err => {
  console.log(err);
})
// { name: 'reject' }

六、类方法-all/allSettled

  1. Promise.all()

所有的Promise都变成fulfilled状态时,才拿到结果。

const p1 = Promise.resolve(202)
const p2 = Promise.resolve(200)
const p3 = Promise.resolve('11')
Promise.all([p1, p2, p3]).then((res) => {
  console.log(res);
})
// [ 202, 200, '11' ]

如果有一个Promise变成了rejected,那么整个Promiserejected状态。

const p1 = Promise.resolve(202)
const p2 = Promise.reject(400)
const p3 = Promise.resolve('11')
Promise.all([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// 400 err

缺点:对于resolved和依然处于pending状态的Promise,获取不到对应的结果。

  1. Promise.allSettled() 在所有的Promise都有结果(settled)后,无论是fulfilled还是reject,才会有最终的状态。并且这个结果一定是fulfilled的。
const p1 = Promise.resolve(202)
const p2 = Promise.reject(400)
const p3 = Promise.resolve('11')
Promise.allSettled([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
/*
[
  { status: 'fulfilled', value: 202 },
  { status: 'rejected', reason: 400 },
  { status: 'fulfilled', value: '11' }
] res
*/

七、类方法-race/any

  1. Promise.race()

竞赛,只要有一个Promise变成fulfilled/rejected状态就结束。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3)
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2)
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(2)
  }, 2000)
})
Promise.race([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// 2 res

如果有一个Promise先变成rejected状态,则整体以rejected状态结束。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3)
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(1)
  }, 1000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(2)
  }, 2000)
})
Promise.race([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// 1 err
  1. Promise.any() 等到一个fulfilled状态才会决定新的Promise状态。
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(3)
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2)
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(1)
  }, 1000)
})
Promise.any([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// 2 res

如果所有的Promise都是rejected状态,那也会等到所有的Promise都变成rejected状态才会结束。

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(3)
  }, 3000)
})
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(2)
  }, 2000)
})
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(1)
  }, 1000)
})
Promise.any([p1, p2, p3]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// [AggregateError: All promises were rejected] { [errors]: [ 3, 2, 1 ] } err

八、Promise实践

1.基本使用:封装接口请求

function request (url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (url === 'foo') {
        resolve('200')
      } else {
        reject('404')
      }
    }, 2000)
  })
}
request('foo1').then(res => {
  console.log(res, 'res---');
}).catch((err) => {
  console.log(err, 'err---');
})

2.Promise.race():请求超时处理

不同于接口统一超时处理,假设某个请求的超时设置小于统一超时,此时可以使用race处理。

function requestImg () {
  return new Promise((resolve, reject) => {
    let img = new Image()
    img.onload = () => {
      // resolve(img)
      setTimeout(() => {
        resolve(img)
      }, 3000)
    }
    img.src = 'https://es6.ruanyifeng.com/images/cover_thumbnail_3rd.jpg'
  })
}
function timeout () {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('请求超时')
    }, 1000)
  })
}
Promise.race([requestImg(), timeout()]).then((res) => {
  console.log(res, 'res');
}).catch((err) => {
  console.log(err, 'err');
})
// 请求超时 err

3.Promise.all():表单接口请求

所有附件上传成功(接口执行完毕)后,再保存表单数据。

九、面试题

待补充......