• promise详解


    promise是异步编程的一种解决方案,比起传统的解决方案——回调函数和事件——更加的强大。

    在项目中,我们经常会遇到异步的问题,在之前,我们通常使用回调函数来解决异步编程,但是在一些比较复杂的情况下,我们经常会陷入回调地狱。promise能够很好的帮助我们解决这个问题。

    早就听说过Promise的大名,但是promise到底是个什么东西呢?是个类?对象?数组?函数?来,先给大家拉出来溜溜。
    在这里插入图片描述
    这里我们可以看到,原来这个promise是一个构造函数,并且在它自己的身上有allrejectresolve这几个特别眼熟的方法,在它的原型上还有thenfinallycatch等方法,也就是说,我们在用Promise new实例化一个对象时,这个对象一定有thenfinallycatch这些方法。

    【Promise的生命周期】

    promise对象有三个状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
    每个Promise都会经历一个短暂的生命周期:先是处于进行中(pending)的状态,此时操作尚未完成,操作结束后,Promise可能会进入到以下两个状态中的其中一个。
    假如异步操作成功完成,进入(fulfilled)状态;假如异步操作没有成功,进入(rejected)状态。

    注意:promise有一个特点,就是一旦状态改变,就不会再改变,Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。这个时候,再给promise添加回调函数就会立即调用,这一点就和事件回调很不一样,事件回调是在错过事件之后再去监听是无法得到结果的。

    【应用】

    我们先来试一下,改写一个普通方法变为一个promise实例,更加方便大家的理解。
    原方法如下:

    export function getInfo () {
      return '返回一个字符串'
    }
    
    • 1
    • 2
    • 3

    我们在调用的时候:

    // 原方法调用如下
    const str = getInfo()
    console.log(str)
    
    • 1
    • 2
    • 3

    修改成promise实例之后:

    export function getInfo () {
      return new Promise((resolve, reject) => {
        const str = '返回一个字符串'
        if (str === undefined) {
          //抛出异常
          reject(new Error('异常:空字符串'))
        }
        //继续执行
        resolve(str)
      })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    // 方法调用如下
    getInfo.then(str => {
    	console.log(str)
    })
    
    • 1
    • 2
    • 3
    • 4

    【基本用法】

    const promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我们可以看到promise接收了一个函数作为参数,这个函数有两个参数,分别是resolve和reject,他们是js引擎自己提供的,不用我们来配置。

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    promise实例生成之后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5

    then方法接受了两个参数,第一个参数是当promise被resolve之后执行的方法,第二个参数是promise被rejected之后执行的方法。这两个参数都是可选的,不一定都要写,他们两个都可以接受promise传递出来的参数。

    【promise在创建之后就会立即执行】(这也是为什么我们经常将promise作为一个函数的返回值来使用)

    let promise = new Promise(function(resolve, reject) {
      console.log('Promise');
      resolve();
    });
    
    promise.then(function() {
      console.log('resolved.');
    });
    
    console.log('Hahahahah');
    
    // Promise
    // Hahahahah
    // resolved
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    上面的代码中,promise对象创建之后立即执行,所以先输出promise,但是resolve指定的回调函数会在当前脚本的同步任务全部执行完之后再执行,所以会先输出Hahahahah,最后输出resolved。
    【关于promise传递给回调函数的参数】
    我们知道,在promise执行完之后可以给回调函数传递参数,如果是rejected,一般会给回调函数传递一个错误的实例;如果是resolve,除了传递一般的普通的正常值以外,有时候我们还能看到传递另一个promise实例参数的情况。

    const p1 = new Promise(function (resolve, reject) {
      // ...
    });
    
    const p2 = new Promise(function (resolve, reject) {
      // ...
      resolve(p1);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    上面代码中,p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。

    注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

    给大家看一个实例:

    const p1 = new Promise(function (resolve, reject) {
      setTimeout(() => reject(new Error('fail')), 3000)
    })
    
    const p2 = new Promise(function (resolve, reject) {
      setTimeout(() => resolve(p1), 1000)
    })
    
    p2
      .then(result => console.log(result))
      .catch(error => console.log(error))
    // Error: fail
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

    【常用方法】

    【Promise.prototype.then()】
    从表面上看,Promise只是能够简化层层回调的写法,而实质上,Promise的精髓是“状态”,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多。所以使用Promise的正确场景是这样的:

    runAsync1()
    .then(function(data){
        console.log(data);
        return runAsync2();
    })
    .then(function(data){
        console.log(data);
        return runAsync3();
    })
    .then(function(data){
        console.log(data);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样采用then的链式用法,就解决了回调地狱的问题。
    【Promise.prototype.catch()】
    Promise.prototype.catch()用于在promise返回错误时的回调函数

    getJSON('/posts.json').then(function(posts) {
      // ...
    }).catch(function(error) {
      // 处理 getJSON 和 前一个回调函数运行时发生的错误
      console.log('发生错误!', error);
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这个时候我们有问题了,我们的then不是也有这样的作用吗?then()的第二个参数就是promise执行失败的时候的回调函数啊。

    // bad
    promise
      .then(function(data) {
        // success
      }, function(err) {
        // error
      });
    
    // good
    promise
      .then(function(data) { //cb
        // success
      })
      .catch(function(err) {
        // error
      });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。因此,建议总是使用catch()方法,而不使用then()方法的第二个参数。
    【Promise.prototype.finally()】

    promise
    .then(result => {···})
    .catch(error => {···})
    .finally(() => {···});
    
    • 1
    • 2
    • 3
    • 4

    【Promise.all()】
    Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

    const p = Promise.all([p1, p2, p3]);
    
    p的状态由p1、p2、p3决定,分成两种情况。
    
    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    
    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    【Promise.race() 】
    Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

  • 相关阅读:
    前端设计模式——过滤器模式
    基于SSM的金鱼销售平台
    同花顺_代码解析_技术指标_T、U
    金山云团队分享 | 5000字读懂Presto如何与Alluxio搭配
    H12-831题库(华为HCIP认证题库)
    vue2 过滤器 自定义指令
    Java,快速排序
    gnss、gps、imu、rtk、ins区分及含义
    docker镜像仓库迁移
    HarmonyOS应用窗口管理(Stage模型)
  • 原文地址:https://blog.csdn.net/lxk116688/article/details/126152435