含义

首先,明白「promise 是异步编程的解决方案」。
可以理解为是一个“保存着未来才会结束的事件(通常是一个异步操作)的结果”的容器。
从语法上,Promise是一个构造函数,它的实例是对象。

特点

1、三种常见状态:
pending(进行中)、fulfilled(已成功)、rejected(已失败)
2、promise对象状态不受外界影响,只有异步操作的结果可以决定是哪种状态。
3、promise对象状态一旦改变,就不会再变。
只有两种可能:pending -> fulfilled、pending -> rejected
一旦发生,状态就凝固了。称为 resolved(已定型)。

缺点

1、Promise一旦创建无法取消。
2、不设置回调函数,得不到promise内部抛出的错误。(下述catch中会详细解释)。
3、penging 状态时,无法感知进行到哪一阶段(刚开始or快结束)。

用法

基础语法

1、参数:function(resolve,reject){}

  1. const timeOut = new Promise(function(resolve,reject){
  2. setTimeout(resolve, 10, 'done1')
  3. })
  4. timeOut.then(function(value){
  5. console.log(value)
  6. })
  7. // 'done'
  1. resolve,reject 可选
  2. setTimeout的第三个(及以上)参数,会给第一个函数当入参
  3. resolve的参数会传给 回调函数

2、执行顺序

  1. const timeOut = new Promise(function(resolve,reject){
  2. console.log('1')
  3. setTimeout(resolve, 10, 'done1')
  4. console.log('2')
  5. })
  6. timeOut.then(function(value){
  7. console.log(value)
  8. })
  9. console.log('3')
  10. // ‘1’
  11. // ‘2’
  12. // ‘3’
  13. // ‘done’
  1. promise一旦创建后,会立即执行。然后当前脚本的所有同步任务执行完才会执行回调函数。
  2. 调用reslove、reject后,promise不会结束执行。所以通常在此时调用return

3、resolve的参数 是 promise时,其状态由返回的promise决定;
reject 参数 是promise时,其状态不受影响。

  1. const p1 = new Promise (function(resolve, reject){
  2. setTimeout(reject('p1 error'), 1000)
  3. })
  4. const p2 = new Promise (function(resolve, reject){
  5. resolve(p1)
  6. })
  7. p2.then(function(value){
  8. console.log('success:',value)
  9. },function(error){
  10. console.log('error:',error)
  11. })
  12. // ‘error:p1 error’
  13. // 虽然p2的状态是resolve,但由于返回的是另一个promise p1,其状态在1s后便无效,更新为 p1的状态
  1. const p1 = new Promise (function(resolve, reject){
  2. setTimeout(resolve('p1 success'), 10)
  3. })
  4. const p2 = new Promise (function(resolve, reject){
  5. reject(p1);
  6. })
  7. p2.then(function(value){
  8. console.log('success:',value)
  9. },function(error){
  10. console.log('error:',error)
  11. })
  12. // ‘error:’,Promise {<resolved>: "p1 success"}
  13. //如果 p2状态是rejected,p1无论咋搞都没用

Promise.prototype.then()

1、then 方法 第一个参数是 resolved状态时的回调函数,第二个参数是rejected状态时的回调函数。都可选。
2、then 方法返回的 仍是 promise实例。所以可以采用链式写法
链式then,可以指定一组按照次序执行的异步操作。
前一个回调函数有可能返回一个promise对象,后一个回调函数就会等待该 promise对象的状态发生变化,才会被调用。(具体参考拓展用法2)

  1. const p1 = new Promise (function(resolve, reject){
  2. setTimeout(resolve('p1 success'), 10)
  3. })
  4. const p2 = new Promise (function(resolve, reject){
  5. resolve('p2 success')
  6. })
  7. p2.then(function(value){
  8. console.log('success:',value)
  9. return p1
  10. },function(error){
  11. console.log('error:',error)
  12. return error
  13. }).then(function(v){
  14. console.log(v)
  15. })
  16. //'success: p2 success'
  17. //‘p1 success'’

Promise.prototype.catch()

1、调用reject,异步操作抛出错误(throw new Error(‘test’)),都会被catch方法捕获
2、在resolve后面抛出错误,不会被捕获。(因为promise状态一旦被改变就不会再变)
3、promise错误会一直向后传递,直到被捕获。(这个捕获包括then的第二个参数,catch方法)

  1. getJSON('/post/1.json').then(function(post) {
  2. return getJSON(post.commentURL);
  3. }).then(function(comments) {
  4. // some code
  5. }).catch(function(error) {
  6. // 处理前面三个Promise产生的错误
  7. // 因为第一个状态reject,第二个同样,直到被捕获
  8. });

一般来说,不要在then() 方法里定义 reject状态的回调函数(then的第二个参数),最好用catch方法

  1. promise
  2. .then(function(data) {
  3. // success
  4. }, function(err) {
  5. // error
  6. });
  7. // 用下面的,不要用上面的
  8. promise
  9. .then(function(data) {
  10. // success
  11. })
  12. .catch(function(err) {
  13. // error
  14. });

4、”promise 会吃掉错误”。
就是说,在promise内部发生错误,程序不会退出进程,终止脚本执行

  1. const p2 = new Promise (function(resolve, reject){
  2. resolve(x+2)
  3. })
  4. p2.then(function(value){
  5. console.log('success:',value)
  6. },function(error){
  7. console.log('error:',error)
  8. return error
  9. }).catch(function(error){
  10. console.log('catch error',error)
  11. })
  12. setTimeout(() => { console.log(123) }, 2000);
  13. //error: ReferenceError: x is not defined
  14. // 123

浏览器运行到错误代码那行,会打印错误提示,但promise内部错误不会影响promise外部的代码。
这里就是上述提到的 缺点2
5、catch 返回一个新的promise,可以接着 then() or catch()

Promise.prototype.finally()

1、不管promise对象最后的状态如何都会执行的操作。
2、finally 的回调函数不接受任何参数,所以不能在此处感知promise的状态。

  1. const p2 = new Promise (function(resolve, reject){
  2. reject('p2 error')
  3. })
  4. p2.then(function(value){
  5. console.log('success:',value)
  6. }).finally(function(v){
  7. console.log('finally',v)
  8. })
  9. // finally undefined

Promise.all()

将多个Promise实例,包装成一个新的Promise实例。

  1. const p = Promise.all([p1, p2, p3]);

1、p 的状态由 p1、p2、p3决定:

  1. 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled。p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
  2. 只要p1、p2、p3有一个被rejected,p的状态就变成rejected。此时第一个被rejected的实例的返回值,传给p的回调函数

2、详细解读上述两条原则:

  1. 多个promise实例rejected时,第一个作为返回值,指的执行快的的第一个 ```javascript const p2 = new Promise (function(resolve, reject){ reject(‘p2 error’) }) const p1 = new Promise (function(resolve, reject){ // setTimeout(reject(‘p1 error’), 10000) 这种延时无效,待查 setTimeout(()=>{reject(‘p1 error’)}, 10000) }) const p3 = new Promise(function(resolve){ setTimeout(resolve(‘p3 success’,2000)) })

    Promise.all([p1 , p2, p3]).then((result)=>{ console.log(‘all result’,result) }).catch((error)=>{ console.log(‘all catch:’, error) })

//all catch: p2 error

//按理说应该时p2先的,不知道为什么 //确实是p2,因为上述延时无效

  1. 2. 如果某个promise实例自己catch错误后,则对于Promise.all收到的就是fulfilled
  2. ```javascript
  3. const p1 = new Promise (function(resolve, reject){
  4. setTimeout(resolve('p1 success'), 10)
  5. })
  6. const p2 = new Promise (function(resolve, reject){
  7. reject('p2 error')
  8. }).catch((error)=>{
  9. console.log('p2 catch:', error)
  10. })
  11. const p3 = new Promise(function(resolve){
  12. setTimeout(resolve('p3 success',2000))
  13. })
  14. Promise.all([p1 , p2, p3]).then((result)=>{
  15. console.log('all result',result)
  16. }).catch((error)=>{
  17. console.log('catch:', error)
  18. })
  19. // p2 catch: p2 error
  20. // all result ["p1 success", undefined, "p3 success"]
  21. //不难理解,p2 catch 后其返回的也是一个promise,(因为catch的返回值也是promise)此时的状态时fulfilled,所以就执行了resolved的回调函数。
  22. //如果此时catch返回个值,也会被all 接收到
  23. const p2 = new Promise (function(resolve, reject){
  24. reject('p2 error')
  25. }).catch((error)=>{
  26. console.log('p2 catch:', error)
  27. return false
  28. })
  29. // p2 catch: p2 error
  30. // all result ["p1 success", false, "p3 success"]
  31. //是的 false也是返回值

Promise.race()

同样是将多个Promise实例,包装成一个新的Promise实例。

  1. const p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
race的用例:如果指定时间没有获得结果,就将promise的状态变为rejected,否则变回resolved。拓展用法1替代写法。

  1. const p = Promise.race([
  2. fetch('/resource-that-may-take-a-while'),
  3. new Promise(function (resolve, reject) {
  4. setTimeout(() => reject(new Error('request timeout')), 5000)
  5. })
  6. ]);
  7. p.then(console.log).catch(console.error);

Promise.allSettled()

这个方法像是针对 all 方法提出的更全面功能的用法。大家都知道,all接收的promise里一旦有一个变成了 rejected,整个状态都变成了rejected,且拿不到其他 fulfilled状态的promise值(当然,你可以把所有的promise都加上catch转为 resolved,但麻烦)。
现在 allSettled(),ES2020新引入,它可以等所有的异步操作都结束了,不管是rejected 还是 fulfilled状态,然后拿到异步操作的结果组成一个数组,再执行下一步。

  1. const p1 = new Promise (function(resolve, reject){
  2. setTimeout(()=>{resolve('p1 success')}, 1000)
  3. })
  4. const p2 = new Promise (function(resolve, reject){
  5. reject('p2 error')
  6. })
  7. const p3 = new Promise(function(resolve){
  8. setTimeout(()=>{resolve('p3 success')},2000)
  9. })
  10. Promise.allSettled([p1,p2,p3]).then((result)=>{
  11. console.log('allSettled result:',result)
  12. })

image.png
成功会有 value属性,失败会有reason属性

Promise.any()

ES2021加入any 方法。
这个方法像是针对all方法 提出的反向用法。

  1. 只要参数实例有一个变成 fulfilled状态,包装实例就会变成fulfilled 状态。
  2. 只有所有实例变成rejected状态,包装实例才会变成rejected状态。

嚯我的浏览器编译不了 any。。。

Promise.resolve()

可以将现有对象转为 Promise对象

Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

  1. const p = Promise.reject('出错了');
  2. // 等同于
  3. const p = new Promise((resolve, reject) => reject('出错了'))
  4. p.then(null, function (s) {
  5. console.log(s)
  6. });

拓展用法

1、解决某些异步长时间pengding无法进行下一步问题,使用promise和setTimeout再封装一个promise

  1. fetchLocation(){
  2. return new Promise((resolve, reject) => {
  3. let timeOut;
  4. window.tpPlus.getLocation().then(res => {
  5. if(res.success){
  6. clearTimeout(timeOut);
  7. resolve(res)
  8. }else{
  9. clearTimeout(timeOut);
  10. reject(res)
  11. }
  12. })
  13. timeOut = setTimeout(() => {
  14. alert('打开定位,可快速查看')
  15. resolve('超时')
  16. },1000)
  17. })
  18. }

2、解决多个异步操作递进调用避免嵌套问题。

  1. async submit(){
  2. const { formVals } = field.getValues();
  3. return new Promise((resolve,reject) => {
  4. // 实名认证
  5. recognitionIdCard(formVals)
  6. .then(() => {
  7. //实人认证
  8. return faceAuth()
  9. })
  10. .then(() => {
  11. return submitApi(formVals)
  12. })
  13. .then(() => {
  14. Toast('提交成功');
  15. resolve()
  16. })
  17. .catch((error) => {
  18. reject(error)
  19. })
  20. })
  21. }

tips: 感谢@noldor(noldor)提供的实践代码

promise相关文档

剖析 Promise 内部结构,一步一步实现一个完整的、能通过所有 Test case 的 Promise 类
最简实现 Promise,支持异步链式调用(20 行)