多回调问题
前端编程时,大多通过接口交换数据,接口调用都是异步的,处理数据都是在回调函数里.
假如需要为一个用户建立档案,需要准备以下数据,然后调用建档接口
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] 之后的方法通过此属性可访问之前方法返回的结果.
js多回调函数
原文地址:https://www.cnblogs.com/mirrortom/p/9333638.html