• 【JS Promise】使用promise一定要注意的几个问题


    读完此文对promise一定会有新的认识。

    1. promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败);
    2. promise状态改变只有两种可能:从pending变为fulfilled和从pending变为rejected。状态一旦改变,就不会再变;
    3. 为了方便描述,一般将fulfilled状态称为resolved

    1. 如何改变promise的状态?

    • (1) resolve(value):如果当前是pending,则变为resolved
    const p1 = new Promise((resolve, reject) => {
      resolve('success');
    });
    
    console.log(p1); // Promise {: 'success'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • (2) reject(reason):如果当前是pending,则变为rejected
    const p2 = new Promise((resolve, reject) => {
      reject('err');
    });
    
    console.log(p2); // Promise {: 'err'}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • (3) 抛出异常:如果当前是pending,则变为rejected
    const p3 = new Promise((resolve, reject) => {
      throw new Error('err');
    });
    
    console,.log(p3) // Promise {: Error: err
    
    • 1
    • 2
    • 3
    • 4
    • 5

    总结:改变promise的状态有三种方式(调用resolve、调用reject和抛出异常)。

    2. 一个promise同时指定多个成功/失败回调,都会调用吗?

    • 成功的情况
    const p = new Promise((resolve, reject) => {
      resolve('success');
    });
    
    p.then((value) => {
      console.log('value1:', value);
    });
    p.then((value) => {
      console.log('value2:', value);
    });
    p.then((value) => {
      console.log('value3:', value);
    });
    
    /* 执行结果:
    value1: success
    value2: success
    value3: success
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 失败的情况
    const p = new Promise((resolve, reject) => {
      reject('err');
    });
    
    p.then(
      (value) => {},
      (reason) => {
        console.log('reason:1', reason);
      },
    );
    p.then(
      (value) => {},
      (reason) => {
        console.log('reason:2', reason);
      },
    );
    p.then(
      (value) => {},
      (reason) => {
        console.log('reason:3', reason);
      },
    );
    
    /* 执行结果:
    reason:1 err
    reason:2 err
    reason:3 err
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    总结:一个promise同时指定多个成功/失败回调函数,当promise改变为对应的状态时都会调用。

    3. promise的状态改变和回调函数的指定谁先谁后?

    我们知道promise在状态改变时会调用对应的回调函数,但在不同的情况下两种还是有先后顺序的。

    • 先指定回调函数,后改变状态的情况:
    const p = new Promise((resolve, reject) => {
      // 执行异步任务,这里用setTimeout模拟,实际上可以是ajax请求
      setTimeout(() => {
        resolve('success'); // 2. 后改变状态(同时指定数据),异步执行回调函数
      }, 1000);
    });
    
    p.then((value) => { // 1. 先指定回调函数,保存当前指定的回调函数(代码底层)
      console.log('value:', value);
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 先改变状态,后指定回调函数的情况:
    const p = new Promise((resolve, reject) => {
      resolve('success'); // 1. 先改变状态(同时指定数据)
    });
    
    p.then((value) => { // 2. 后指定回调函数,异步执行回调函数
      console.log('value:', value);
    });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    或者:

    const p = new Promise((resolve, reject) => {
      // 执行异步任务,这里用setTimeout模拟,实际上可以是ajax请求
      setTimeout(() => {
        resolve('success'); // 1. 先改变状态(同时指定数据)
      }, 1000);
    });
    
    setTimeout(() => {
      p.then((value) => { // 2. 后指定回调函数,异步执行回调函数
        console.log('value:', value);
      });
    }, 1001); // 时间比上面晚一点即可
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    总结:

    • 两者都有可能,一般情况(执行异步任务)时先指定回调再改变状态;
    • 如何先改变状态再指定回调函数?
      • 在执行器中直接调用resolve/reject
      • 延迟更长时间再调用then
    • 什么时候才得到数据?
      • 若先指定回调,那当状态改变时,回调函数就会执行,从而得到数据
      • 若先改变状态,那当指定回调时,回调函数就会执行,从而得到数据

    4. promise.then 返回的新promise的结果状态由什么决定?

    我们知道promise是支持链式调用的,比如下面的情况:

    const p = new Promise((resolve, reject) => {
      resolve('success');
    });
    
    p.then(
      (value) => {
        console.log('value1:', value);
      },
      (reason) => {
        console.log('reason1:', reason);
      },
    )
      .then(
        (value) => {
          console.log('value2:', value);
        },
        (reason) => {
          console.log('reason2:', reason);
        },
      )
      .then(
        (value) => {
          console.log('value3:', value);
        },
        (reason) => {
          console.log('reason3:', reason);
        },
      );
    
    
    /* 执行结果:
    value1: success
    value2: undefined
    value3: undefined
    /*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    为什么后面两个回调的执行结果是undefined呢,因为新的promise的结果状态是由then()指定的回调函数执行的结果决定的。可以看到第一个then方法中回调函数没有指定返回值,那么就会默认返回undefined,从而导致后面的then方法的回调函数的执行结果都为undefined

    那要想后面两个then方法的回调函数的执行结果有值,可以做如下修改:

    p.then(
      (value) => {
        console.log('value1:', value);
    +    return 'hello';
      },
      (reason) => {
        console.log('reason1:', reason);
      },
    )
      .then(
        (value) => {
          console.log('value2:', value);
     +     return Promise.resolve('world');
        },
        (reason) => {
          console.log('reason2:', reason);
        },
      )
      .then(
        (value) => {
          console.log('value3:', value);
        },
        (reason) => {
          console.log('reason3:', reason);
        },
      );
    
    /* 执行结果:
    value1: success
    value2: hello
    value3: world
    /*
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    体会一下下面代码的输出结果:

    const p = new Promise((resolve, reject) => {
      reject('err');
    });
    
    p.then(
      (value) => {
        console.log('value1:', value);
        return 'hello';
      },
      (reason) => {
        console.log('reason1:', reason);
        throw 'err2';
      },
    )
      .then(
        (value) => {
          console.log('value2:', value);
          return Promise.resolve('world');
        },
        (reason) => {
          console.log('reason2:', reason);
          return 1000;
        },
      )
      .then(
        (value) => {
          console.log('value3:', value);
        },
        (reason) => {
          console.log('reason3:', reason);
        },
      );
    /* 执行结果:
    reason1: err
    reason2: err2
    value3: 1000
    *
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    总结:新的promise(记为promise1)的结果状态:

    • 简单来说:由then()指定的回调函数执行的结果决定。
    • 详细来讲:
      • 若抛出异常:promise1变为rejected ,reason的值为抛出的异常;
      • 若返回的是非promise的任意值:promise1变为resolved ,value的值为返回的任意值;
      • 若返回的是另一个新的promise:此promise的结果就会成为promise1的结果。

    5. promise如何串联多个任务?

    • promise.then()会返回一个新的promise,可以写成then()的链式调用;
    • 通过then()的链式调用来串联多个同步/异步任务。

    看下面这段代码:

    new Promise((resolve, reject) => {
      // 模拟异步任务
      setTimeout(() => {
        console.log('执行任务1(这是异步的)');
        resolve(100);
      }, 1000);
    })
      .then((value) => {
        console.log('任务1的返回结果:', value);
    
        console.log('执行任务2(这是同步的)');
        return Promise.resolve(200);
      })
      .then((value) => {
        console.log('任务2的返回结果:', value);
    
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            console.log('执行任务3(这是异步的)');
            resolve(300);
          }, 1000);
        });
      })
      .then((value) => {
        console.log('任务3的返回结果:', value);
      });
    
    /* 执行结果:
    执行任务1(这是异步的)
    任务1的返回结果: 100
    执行任务2(这是同步的)
    任务2的返回结果: 200
    执行任务3(这是异步的)
    任务3的返回结果: 300
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    可以看到3个任务不论同步还是异步,都是按顺序执行的。

    需要注意的是:若想在then()中继续执行异步任务,则用promise包装,在其中调用新promise的resolve方法来讲值传递出去。

    6. promise的异常穿透

    • 当使用then链式调用时,可以在最后在指定失败的回调;
    • 当前面任何操作出了异常,都会传到最后失败的回调中处理。
    new Promise((resolve, reject) => {
      reject('err');
    })
      .then((value) => {
        console.log('value1:', value);
      })
      .then((value) => {
        console.log('value2:', value);
      })
      .then((value) => {
        console.log('value3:', value);
      })
      .catch((reason) => {
        console.log('reason:', reason);
      });
    
    /* 执行结果:
    reason: err
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    上面的这段代码其实相当于:

    new Promise((resolve, reject) => {
      reject('err');
    })
      .then(
        (value) => {
          console.log('value1:', value);
        },
        (reason) => {
          throw reason;
        },
      )
      .then(
        (value) => {
          console.log('value2:', value);
        },
        (reason) => {
          throw reason;
        },
      )
      .then(
        (value) => {
          console.log('value3:', value);
        },
        (reason) => {
          throw reason;
        },
      )
      .catch((reason) => {
        console.log('reason:', reason);
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    reason会逐级往下传递,直到最后的catch。

    7. 如何中断promise链?

    当使用promise.then链式调用时,若在中间过程中断,后面的then方法就不会再调用。

    new Promise((resolve, reject) => {
      reject('err');
    })
      .then((value) => {
        console.log('value1:', value);
      })
      .then(
        (value) => {
          console.log('value2:', value);
        },
        (reason) => {
          return new Promise(() => {});
        },
      )
      .then((value) => {
        console.log('value3:', value);
      })
      .catch((reason) => {
        console.log('reason:', reason);
      });
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    上面的代码最后没有任何输出。由于在第二个then方法的rejected回调中返回了一个pending状态的promise对象,因此后续的then方法也就不会再执行了。

  • 相关阅读:
    oracle varchar2类型如何转化为date类型
    大数据面试题
    pytorch代码实现之空间通道重组卷积SCConv
    3D靓图!CEEMDAN-Kmeans-VMD-CNN-BiLSTM-Attention双重分解卷积双向长短期注意力多元时序预测
    IMX6ULL移植篇-Linux内核源码文件表
    Java实战发包到远程maven仓库
    类的加载(也叫类的初始化)和对象初始化
    改进方法实验测试
    问题:anaconda的bin和envs目录莫名奇妙消失!
    《统计学习方法》第十九章 马尔可夫链蒙特卡罗法
  • 原文地址:https://blog.csdn.net/baidu_36511315/article/details/126075547