• js多回调函数


    多回调问题

    前端编程时,大多通过接口交换数据,接口调用都是异步的,处理数据都是在回调函数里.

    假如需要为一个用户建立档案,需要准备以下数据,然后调用建档接口

    name     // 用户名字 使用接口 get_name(userid)

    files       // 用户附件  使用接口 get_atta(userid,name,location)

    create_record(userid,name,location) // 调用建档接口

    create_files(recordId,userid,files)   // 上传用户文件

    要解决这个问题,可以先调用name接口,在其回调里调用addr接口,然后在addr的回调里调用create_record.....

    这难以想象,代码会写得很长,回调嵌套多层.虽然能解决问题,但是,程序代码顺序执行的思维,很难理清这种问题,如果回调再多一些,代码就难以维护.一看就头晕.

    也许同步是入门异步是进阶,前者看,后者思

    同步方法

    依然使用程序顺序执行的思路,将代码顺序写出,(假如接口是同步得到结果的)

    function createRecord(userid){
      var
    name = get_name(userid);   var files = get_atta(userid,name,location);   var recordId = create_record(userid,name,location);   create_files(recordId,userid,files);
    }

    异步方法时,不会得到正确结果,但如果设法让上述代码顺序执行,执行完第1个方法再执行第2个...,也就是同步的思路.那么就能得到正确结果.

    顺序等待

    等待前面的方法回调后,再执行后面的方法.

      let user = { userid: 1 } // 保存参数
      let isOk = false;// 全部成功时,设为true
      //
    function createRecord() {
       // 获取名字
    if (!user.hasOwnProperty('name')) {
        // 异步方法 参数1:数据 2:成功时执行 3:失败时执行 get_name(user, (res)
    => {
    user.name
    = res.Name;
          console.log('获取名字成功');
          // 成功得到结果后,递归调用.由于if条件,递归时不会重复执行 createRecord(); }, (err)
    => { // 不成功时,方法结束 });
        // 保证异步成功返回时,才执行后续方法
    return; }
    // 获取文件数据
    if (!user.hasOwnProperty('files')) { get_atta(user, (res) => { user.files = res.Attas;
          console.log('获取文件成功') createRecord(); }, (err)
    => { // }); return; } // 建档接口,返回ID if (!user.hasOwnProperty('recordId')) { .....return; } // 调用文档接口,有文件时才上传 if (user.files.length > 0) { create_files(user, (res) => {
          // 最后一个接口,当成功返回时,表示全部请求成功了
          console.log('上传文件成功') isOk
    = true; }, (err) => { // }); } else { isOk = true; } }

    每次递归时,只会执行其中一个IF块,IF块就是接口调用,当成功返回时,刷新IF条件,继续递归,失败时不递归.

    如此,模拟了"顺序执行".在思路上比较清晰,和同步方法顺序执行的思路一至.

    此法缺点多,首先此法是同步接口方法的在形式上的生搬硬套.将所有请求接口的方法按顺序写在一个方法内,使用IF条件判断是否执行,使用递归反复执行.

    IF块代码较长时,较多时,整个方法就很长,难以维护.方法作用旨在"控制每个接口方法的顺序执行",只是个代码的容器.

    条件逻辑复杂,如果判定条件写错了,或者忘了刷新判定条件,很容易造成无限递归,程序崩溃.

    顺序调用

    将IF块写成独立方法,在其中调用下一个步骤的方法,直到调用最后一个方法.

      let user = { userid: 1 } // 保存参数
      let isOk = false;// 全部成功时,设为true
      // 获取名字
      function getName(user)
      {
        get_name(user, (res) =>
        {
          user.name = res.Name;
          // 成功后,调用获取文档方法
          getAtta(user);
        });
      }
      // 获取文档
      function getAtta(user)
      {
        get_atta(user, (res) =>
        {
          user.files = res.Attas;
          // 成功后,调用建档方法
          createRecord(user);
        })
      }
      // 建立档案
      function createRecord(user)
      {
        create_record(user, (res) =>
        {
          user.recordId = res.RecordId;
          // 成功后,调用上传文件方法
          createFiles(user);
        })
      }
      // 上传文档方法
      function createFiles(user)
      {
        if (user.files.length == 0)
        {
          isOk = true;
          return;
        }
        create_files(user, (res) =>
        {
          isOk = true;
        })
      }
      // 调用
      getName(user);

    这个办法与递归法相比,不将所有方法写到一个方法内,反而拆分为独立方法,每个方法再调用下个方法.最后完成所有请求.

    没有条件语句逻辑更清晰明了,各接口方法完成各自取数据任务,有一个起始方法,和最后一个结束方法,链环式的.按顺序一个接一个调用的.如果其中一个出错,那么终止.

    各方法独立,依然按照递归法的顺序调用后续方法,相比所有方法写在一起的递归法,只是在形式上分开了,也许好维护些,但缺少"封装"性.

    容器方法

    结合前两种办法的特点,造一个类管理方法的执行,返回值以及出错信息.

        function moreAjax()
        {
          let self = this;
          // 全部成功时为true
          self.AllOk = false;
    
          //  方法容器
          let ajaxList = [];
    
          // 每个方法成功时返回值容器
          self.ResList = [];
    
          // 每个方法错误时返回值容器
          self.ErrList = [];
    
          // 方法执行序列
          let index = 0;
    
          // 添加方法
          self.add = function (method)
          {
            ajaxList.push(method);
          }
    
          // 开按执行 按add时顺序执行
          self.start = function ()
          {
            if (index == ajaxList.length)
            {
              // 全部成功标识
              self.AllOk = true;
              return;
            }
            //
            ajaxList[index](
              (res) =>
              {
                console.log('(success)执行序号' + index);
                // 保存回调结果
                self.ResList.push(res);
                // 递归调用,执行下个方法
                index = index + 1;
                self.start();
              },
              (err) =>
              {
                console.log('(error)执行序号' + index);
                // 保存出错结果
                self.ErrList.push(err);
              });
          }
        }
    
        // 调用
        let test = new moreAjax();
        // 添加方法
        test.add((success, error) =>{
             get_name(
                 (res)=>{success(res)},
                 (err)=>{success(err)}
             )
        })
        // 再添加方法
        test.add((success, error) =>{
             get_location(
                 (res)=>{success(res)},
                 (err)=>{success(err)}
             )
        })
        // 添加最后一个方法
        test.add((success, error) =>{
             success("lastMethod");
             if (send.AllOk)
             {
                 console.log(send.ResList);
             } else
             {
                 console.log(send.ErrList);
             }
        })
        // 启动
        test.start();

    容器法是前两种办法的优化,兼顾"封装"性和灵活性.

    add方法添加接口方法,方法要求前两个参数第1个用于成功时执行,第2个用于失败时执行. 每调用一次add,则向ajaxList加入一个方法.

    start方法执行后,方法会按add的顺序开始执行,每个方法执行完成时,若成功会记录返回值,若失败记录失败信息.通过ResList和ErrList数组属性,访问这些值.

    如果每个方法都正确返回,那么会执行到ajaxList里的最后一个方法,如果其中一个失败,则会停止.

    每个方法成功时,ResList增加一个结果.第一个方法成功时,结果值为 ResList[0] 之后的方法通过此属性可访问之前方法返回的结果.

  • 相关阅读:
    筛选法 || POJ 1356 Prime Land
    搜索 || BFS || POJ 3278 Catch That Cow
    (素数筛) 找质数
    (map)后缀字符串
    字符串的进制
    (二进制枚举子集)买玩具
    (基础)01背包问题
    (基础)编辑距离
    (基础)最长公共字串
    最大子矩阵和
  • 原文地址:https://www.cnblogs.com/mirrortom/p/9333638.html
Copyright © 2020-2023  润新知