zoukankan      html  css  js  c++  java
  • 547 Promise:Ajax 的串行、并行, Promise的executor和状态,then、catch、finally,then链

    Promise

    ES6 语法规范中新增加的内置类,用来处理 JS 中异步编程的,而我们所谓的 promise 设计模式,就是基于 promise 对异步操作进行管理。

    对Ajax中的串行、并行导致的回调地狱、其他乱七八糟的事,进行管控。

    我的理解:执行Promise的执行器函数executor过程中,如果执行了resolve函数,表示异步操作成功,把PromiseStatus改为fulfilled / resolved,Promise实例的then中执行resolved对应的方法;如果执行了reject函数,表示异步操作失败,把PromiseStatus改为rejected,Promise实例的then中执行rejected对应的方法。

    在执行异步操作,等待的过程中,调用 实例.then(),往事件池【不是事件池】中添加 异步成功、失败对应的方法。【 .then()时,还不会执行.then()中的方法,只是把方法加进去。】

    异步操作结束后,根据PromiseStatus,执行.then()中对应的方法。

    实例.then():有2个方法,到底执行哪个方法,由当前实例的状态、value决定【value无关】。如果是executor函数,那就看执行resolve 还是 reject;如果不是executor函数,是then()中的函数,就看函数执行有没有报错,不管执行的是then()中的第几个函数。


    异步编程中的“回调地狱”,Ajax 的串行、并行

    Ajax 的串行

    Ajax串行:只有第一个请求成功才能执行第二个,第二个成功才能执行第三个....最后一个请求成功后拿到了每一次请求的所有数据

    $.Ajax({
      url: '/baseInfo',
      method: 'GET',
      data: {
        name: 'zhanglu'
      },
      success: result => {
        let scoreId = result.scoreId;
    
        $.Ajax({
          url: '/scoreInfo',
          method: 'GET',
          data: {
            id: scoreId
          },
          success: result => {
            let chinese = result.chinese;
    
            $.Ajax({
              url: '/paiming',
              method: 'GET',
              data: {
                num: chinese
              },
              success: result => { }
            });
          }
        });
      }
    });
    

    Ajax 的并行

    Ajax 的并行:请求可以同时发送,但是需要等到所有请求都成功才会做一件事

    // 三个请求可以同时发送,但是需要等到所有请求都成功才会做一件事
    let chi = 100,
      eng = 12,
      math = 98;
    let chiPai,
      engPai,
      mathPai;
    let count = 0;
    
    function func() {
      // func虽然被调用3次,但是count >= 3,才会执行if中的代码
      if (count >= 3) {
        // 处理自己要做的事情
      }
    }
    
    $.Ajax({
      url: '/pai?chi=' + chi,
      success: result => {
        chiPai = result;
        count++;
        func();
      }
    });
    
    $.Ajax({
      url: '/pai?eng=' + eng,
      success: result => {
        engPai = result;
        count++;
        func();
      }
    });
    
    $.Ajax({
      url: '/pai?math=' + math,
      success: result => {
        mathPai = result;
        count++;
        func();
      }
    });
    

    let p1 = new Promise((resolve, reject) => {
        console.log(111)
    })
    console.log(p1)
    

    let p1 = new Promise((resolve, reject) => {
        console.log(111)
        resolve()
    })
    console.log(p1)
    


    Promise基础语法

    executor和状态

    let PromiseExamp = new Promise(); // => Uncaught TypeError: Promise resolver undefined is not a function
    
    
    // --------------------------------------
    
    
    /*
     * new Promise([executor]): 第一个执行函数必须传递
     *   [executor 简称exe]
     *     1.exe是Promise类的一个回调函数,exe函数中放的就是当前要处理的异步操作,Promise内部会把它执行。
     *       new Promise的时候就会把exe执行,创建Promise的一个实例。
     *     2.Promise不仅把exe执行,而且还给exe传递两个参数(两个参数也是函数类型):
     * 		 => resolve函数:它执行代表Promise处理的异步操作是成功的,把Promise的状态改为fulfilled。
     *       => reject函数:它执行代表Promise处理的异步操作是失败的,把Promise的状态改为rejected。
     */
    
    
    let PromiseExamp = new Promise((resolve, reject) => {
      // => 这里一般存放的都是即将要处理的异步任务,任务成功,执行resolve;任务失败,执行reject。当然,写同步的也可以。
      // 【executor不报错,还需要在executor中手动调用resolve,才会执行Promise实例的then函数中的完成处理函数;executor报错,不需要在executor中手动调用reject,就会执行Promise实例的then函数中的拒绝处理函数。】
      let ran = Math.random();
      setTimeout(() => {
        if (ran < 0.5) {
          reject(ran);
          return;
        }
        resolve(ran);
      }, 1000);
    });
    
    PromiseExamp.then(result => {
      // => 状态为fulfilled成功后执行(result:[[PromiseValue]])
      console.log('成功: ' + result);
    }, error => {
      // => 状态为rejected失败后执行
      console.log('失败: ' + error);
    }); 
    


    js中的异常处理

    // js中的异常处理
    console.log(a); // => Uncaught ReferenceError: a is not defined
    let b = 10;
    console.log(b);
    
    // => 在JS中当前行代码报错,会中断主线程的渲染(下面代码将不再执行)
    // throw new Error(''):手动抛出一个异常错误,目的就是让后面代码不再执行
    // 如果上面代码报错,不想让期影响后面的代码,我们需要做异常捕获:try catch finally
    
    try {
      console.log(a);
    } catch (e) {
      // => 错误信息
      console.log(e.message); // a is not defined
    }
    
    try {
      throw new Error('嘻嘻嘻')
    } catch (e) {
      console.log(e.message); // 嘻嘻嘻
    }
    
    try {
      throw '哈哈哈'
    } catch (e) {
      console.log(e); // 哈哈哈
    }
    
    let b = 10;
    console.log(b);
    

    then、catch、finally

    /*
     * Promise.prototype
     *    then: 设置成功或者失败后执行的方法(成功或者失败都可以设置,也可以只设置一个)
     *       pro.then([success], [error])
     * 		 pro.then([success])
     *       pro.then(null, [error])
     *    catch: 设置失败后执行的方法
     *    finally: 设置不论成功还是失败都会执行的方法(一般不用)
     */
    
    let PromiseExamp = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 100);
    });
    PromiseExamp.then(result => {
      console.log('成功: ' + result);
    });
    PromiseExamp.catch(error => {
      console.log('失败: ' + error);
    });
    PromiseExamp.finally(x => {
      console.log('哈哈');
    });
    
    
    // 使用链式写法,则不会像上面那样:ran < 0.5 时,控制台报错
    let PromiseExamp = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 100);
    });
    
    PromiseExamp.then(result => {
      console.log('成功: ' + result);
    }).catch(error => {
      console.log('失败: ' + error);
    }).finally(x => {
      console.log('哈哈');
    });
    
    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 100);
    })
    
    p1.then(result => console.log('成功: ' + result));
    p1.catch(error => console.log('失败: ' + error));
    p1.finally(() => console.log('嘻嘻嘻'));
    
    
    console.log('-----------------------------')
    
    
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 100);
    });
    
    p2.then(result => console.log('成功: ' + result))
      .catch(error => console.log('失败: ' + error))
      .finally(() => console.log('哈哈哈'))
    

    then链

    let pro1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        console.log(ran)
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 100);
    });
    
    // 刚开始,pro1【.then】的状态是pedding。当pro1.then中2个方法中的任意一个执行,报错、成功、失败,只要执行的结果没报错,说明是成功的;可能这里又会返回一个异步操作的结果。2个方法中的任意一个执行,只要成功了,都会把pro3的 完成处理函数 执行;反之,则执行拒绝处理函数。第二次then监听的是 这个then的2个回调函数的执行结果
    let pro2 = pro1.then(result => {
      console.log(`PRO1: SUCCESS`);
    }, error => {
      console.log(`PRO1: ERROR`)
    });
    
    // 当pro2的状态为fulfilled 或 rejected时,执行pro2.then中的某个方法
    let pro3 = pro2.then(result => {
      console.log(`PRO2: SUCCESS`);
    }, error => {
      console.log(`PRO2: ERROR`)
    });
    
    
    
    // ---------------------------------------
    
    
    // => then(onResolve,onReject)
    // 执行then、catch、finally返回的结果是一个全新的Promise实例,所以可以链式写下去;下一个then中哪个方法会被执行,由上一个then中某个方法执行的结果来决定
    // 上一个then中某个方法的返回值会传递给下一个then的某个方法中
    // 实例.then():有2个方法,到底执行哪个方法,由当前实例的状态、value决定【alue无关】。如果是executor函数,那就看执行resolve 还是 reject;如果不是executor函数,是then()中的函数,就看函数执行有没有报错,不管执行的是then()中的第几个函数
    new Promise((resolve, reject) => {
      // resolve(100); // => 把第一个Promise实例的value值改为100/-100
      reject(-100);
    }).then(result => {
      console.log(result);
      // then中RETURN的结果相当于把当前这个新的Promise实例中的value值改为返回值
      return result * 10;
    }, err => {
      console.log(err);
      return err / 10;
    }).then(A => {
      console.log('A:' + A);
    }, B => {
      console.log('B:' + B);
    }).then(C => {
    
    }, D => {
    
    });
    
    
    // ------------------------
    
    
    // 如果当前Promise实例的状态确定后,都会到对应的then中找方法,如果then中没有对应的这个方法,则会向下顺延
    // then(onfulfilled)、 then(null,onrejected)  
    new Promise((resolve, reject) => {
      reject(-100);
    }).then(A => {
      console.log(A);
      return A * 10;
    }).catch(B => {
      console.log(B); // => -100
      return B * 10;
    });
    
    
    
    // --------------------------------
    
    
    // 第一个.then()返回一个新的Promise实例
    new Promise((resolve, reject) => {
      resolve(100);
    }).then(A => {
      // => 执行报错,让.then创建的Promise实例变为失败状态,并且把报错的原因作为此Promise的value值 
      // 【而不是把return 的 A * 10作为返回值,其实这行代码报错,这个then中下面的代码就不会执行了。】
      console.log(AAAAA);
      return A * 10;
    }).catch(B => {
      console.log(B); // => ReferenceError: AAAAA is not defined
      return '@';
    }).then(C => {
      console.log(C); // => '@'
    });
    
    
    // --------------------------------
    
    
    // (1)第一个Promise成功,找第1个then中的第一个函数,没有,不会报错,顺延到下一个成功;(2)第一个catch不代表成功,跳过;(3)找到第2个then中的第一个函数,执行,没报错,成功;(4)第3个then中的第一个函数,执行,没报错,成功;(5)第2个catch不执行;(6)第3个then是成功,第2个catch不执行,所以执行第4个then,报错;(7)执行第3个catch,没东西,顺延;(8)执行最后一个then的拒绝处理函数。
    new Promise((resolve, reject) => {
      resolve();
    }).then().catch(x => {
      console.log(1);
    }).then(x => {
      console.log(2); // => OK
    }).then(x => {
      console.log(3); // => OK
    }).catch(x => {
      console.log(4);
    }).then(x => {
      console.log('AAA'); // => OK
      console.log(AAA); // => 报错
    }).catch().then(null, x => {
      console.log(5); // => OK
    });
    

    promise的应用:解决Ajax的串行、并行

    < !--解决AJAX回调地狱 -->
      function queryBase() {
        return new Promise(resolve => {
          $.ajax({
            url: '/baseInfo?name=zhanglu',
            success: result => {
              resolve(result);
            }
          });
        });
      }
    
    function queryScore(scoreId) {
      return new Promise(resolve => {
        $.ajax({
          url: '/score?id=' + scoreId,
          success: result => {
            resolve(result);
          }
        });
      });
    }
    
    function queryChinese(chinese) {
      return new Promise(resolve => {
        $.ajax({
          url: '/paiming?chin=' + chinese,
          success: result => {
            resolve(result);
          }
        });
      });
    }
    
    queryBase().then(baseInfo => {
      let scoreId = baseInfo.scoreId;
      // => then方法中如果返回的是一个Promise实例,则当前返回实例的成功或者失败状态,影响着下一个then中哪个方法会被触发执行;如果返回的是非Promise实例,则看当前方法执行是否报错,来决定下一个then中哪个方法执行;
      return queryScore(scoreId);
    }).then(scoreInfo => {
      let chinese = scoreInfo.chinese;
      return queryChinese(chinese);
    }).then(pai => {
      console.log('排名是:' + pai);
    });
    
    
    queryBase().then(baseInfo => queryScore(baseInfo.scoreId))
      .then(scoreInfo => queryChinese(scoreInfo.chinese))
      .then(pai => console.log('排名是:' + pai));
    
    async function func() {
      let baseInfo = await queryBase();
      let scoreInfo = await queryScore(baseInfo.scoreId);
      let pai = await queryChinese(scoreInfo.chinese);
      //....
    }
    func();
    
    < !--解决AJAX并行 -->
      function ajax1() {
        return new Promise(resolve => {
          $.ajax({
            url: '/api1',
            // 相当于 success: res => resolve(res)
            success: resolve
          });
        });
      }
    
    function ajax2() {
      return new Promise(resolve => {
        $.ajax({
          url: '/api2',
          success: resolve
        });
      });
    }
    
    function ajax3() {
      return new Promise(resolve => {
        $.ajax({
          url: '/api3',
          success: resolve
        });
      });
    }
    
    // Promise.all([Promise1,Promise2,...]):all中存放的是多个Promise实例(每一个实例管理者一个异步操作),执行all方法返回的结果是一个新的Promise实例"proa"
    // => 当所有Promise实例的状态都为fulfilled的时候(成功),让proa的状态也变为fulfilled,并且把所有Promise成功获取的结果,存储为成为一个数组(顺序和最开始编写的顺序一致)“result=[result1, result2, ...]”,让proa这个数组的value值等于这个数组
    // => 都成功(proa状态是FUFILLED)才会通知then中第一个方法执行,只要有一个失败(proa状态是rejected),就会通知then中第二个方法或者catch中的方法执行
    Promise.all([ajax1(), ajax3(), ajax2()]).then(results => {
      // => results: [result1, result3, result2]
    });
    
    Promise.race([ajax1(), ajax3(), ajax2()]).then(result => {
      // => 看哪一个Promise状态最先处理完(成功或者失败),以最先处理完的为主
    });
    

    报错问题

    let p1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 1000);
    })
    
    p1.then(result => console.log('成功: ' + result))
    p1.catch(error => console.log('失败: ' + error))
    p1.finally(() => console.log('嘻嘻嘻'))
    
    
    console.log('-----------------------------')
    
    
    let p2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        let ran = Math.random();
        ran < 0.5 ? reject(ran) : resolve(ran);
      }, 1000);
    });
    
    p2.then(result => console.log('成功: ' + result))
      .catch(error => console.log('失败: ' + error))
      .finally(() => console.log('哈哈哈'))
    

    练习题

    let promise1 = Promise.resolve()
      .then(res => console.log(1))
      .then(res => console.log(2))
    
    let promise2 = new Promise(resolve => {
      setTimeout(() => {
        console.log(6)
        resolve()
      })
    }).then(res => console.log(3))
    
    async function main() {
      console.log(4)
      console.log(await Promise.all([promise2, promise1]))
      console.log(5)
      return { obj: 5 }
    }
    
    let promise3 = Promise.resolve()
      .then(res => console.log(8))
      .then(res => console.log(9))
    
    console.log(typeof main())
    
    // 4
    // object
    // 1
    // 8
    // 2
    // 9
    // 6
    // 3
    // [ undefined, undefined ]
    // 5
    
  • 相关阅读:
    四套读写方案
    如何保证ArrayList线程安全
    异常总结<经典例题>
    java.移位运算符
    java反射机制
    面试题:return和finally执行
    Spring_通过注解配置 Bean(1)
    Spring_通过 FactoryBean 配置 Bean
    Spring_通过工厂方法配置 Bean
    Spring_管理 Bean 的生命周期
  • 原文地址:https://www.cnblogs.com/jianjie/p/13841108.html
Copyright © 2011-2022 走看看