多回调问题

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

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

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] 之后的方法通过此属性可访问之前方法返回的结果.

最新文章

  1. 使用VS2015进行C++开发的6个主要原因
  2. Echars详解
  3. IDEA UML类图插件
  4. selectors实现高并发
  5. C++实现水波纹、火焰和血浆效果
  6. saltstack实战4--综合练习3
  7. linux非阻塞的socket EAGAIN的错误处理
  8. Cloudera Manager安装
  9. Codeforces Round #334 (Div. 1) C. Lieges of Legendre
  10. 使用c#获取access中所有表的表名与内容
  11. iOS中Block介绍 基础
  12. 笔试题&面试题:CW输出矩阵
  13. html日历(3)
  14. DB2开发系列之四——触发器
  15. sql server 阻塞查询
  16. Python常用模块-时间模块
  17. bottle.py中的SimpleTemplate
  18. input的一些使用方法
  19. P3455 [POI2007]ZAP-Queries(莫比乌斯反演)
  20. 电脑用U盘启动

热门文章

  1. Ubuntu18.04安装netstat
  2. PyCharm中快速给选中的代码加上{}、<>、()、[]
  3. Oracle通过ROWID删除表中重复记录
  4. Django Rest framework 框架
  5. Hbase 架构体系
  6. Prism框架研究(一)
  7. DButils实现数据库表下划线转bean中驼峰格式
  8. 转 在PowerDesigner的PDM图形窗口中显示数据列的中文注释
  9. css3实现背景渐变
  10. orcale三表连接查询