AngularJS中$http响应赋值变量后函数返回undefined问题咨询
你遇到的问题本质是JavaScript异步操作的特性导致的:$http发起的HTTP请求是异步执行的,当你的SelectedTerms函数执行到return data这一行时,后端的响应还没回来,then回调里的data = response.data代码根本还没执行,所以返回的是变量data的初始值(你这里初始定义是var data="",但实际场景中可能因为异步时机问题表现为undefined)。
怎么修复?
我们需要利用AngularJS中$http返回的Promise对象来处理异步结果,或者把依赖返回数据的逻辑移到回调函数内部。下面给你两种常用的解决方案:
方案1:让SelectedTerms返回Promise,调用方通过then获取结果
修改你的函数,直接返回$http调用的结果(它本身就是一个Promise),这样调用这个函数的地方可以通过链式调用then来拿到后端返回的数据:
$scope.SelectedTerms = function (feeHeadId, feeHead) { var formData = { customerId : $scope.customerId, feeHeadId : feeHeadId, selectedOptions : feeHead.feeHeadSelectedOption.display.split("+"), payterms : feeHead.pmntTerms }; return $http({ url : '<%= fileReader.getValue("ICOLLECT_SERVICE") %>?<%=ICollectConstants.FLOW_NAME_PARAM%>=<%= ICollectConstants.FLOW_NAME %>&<%=ICollectConstants.MERC_ID%>=<%= icMerchantMasterBean.getIcMerchantMasterId() %>&<%= ICollectConstants.BANK_ID%>=<%= icMerchantMasterBean.getIcBankMasterId()%>&req_id=validateFeeAndSendResponse&checksum=BFEDE97EF99CCF9C1362EBFE7C94E3233ED6E2B7637AC7BDB34AC916D9BAC3AD&reqtoken=<%=req_token%>'+'&method=TEST', method : "POST", params : { jsondataforST: btoa(angular.toJson(formData)), responseType:"1", backUrl :'<%= backUrl%>', icMerchantMasterId : <%= icMerchantMasterBean.getIcMerchantMasterId()%>, icMerchantBankId : <%= icMerchantMasterBean.getIcBankMasterId()%> } }).then(function Success(response){ if(response.data.statusCode=="100"){ // 这里可以先做一些数据处理 var data = response.data; // 如果需要执行separateTermLogic,直接在这里调用 $scope.separateTermLogic(feeHead, data); // 返回处理后的数据给后续的then调用 return data; } else { // 处理状态码非100的情况,比如返回null或者抛出错误 return null; } }, function error(response){ console.log("error occured", response); // 抛出错误,让调用方可以捕获 throw response; }); };
调用这个函数的地方就可以这样用:
$scope.SelectedTerms(yourFeeHeadId, yourFeeHead).then(function(data) { if(data) { // 这里就能拿到后端返回的有效数据了 console.log("SelectedTerms返回的数据", data); // 在这里做你原本想用return data实现的逻辑 } }).catch(function(error) { // 处理请求失败的情况 });
方案2:将依赖数据的逻辑直接移到$http的回调内部
如果你的业务逻辑不需要把数据返回给外部,而是直接在拿到数据后执行操作(比如你代码里注释掉的$scope.separateTermLogic(feeHead,data)),那最直接的方式就是把所有需要使用data的代码都放到Success回调里,不需要尝试return数据:
$scope.SelectedTerms = function (feeHeadId, feeHead) { var formData = { customerId : $scope.customerId, feeHeadId : feeHeadId, selectedOptions : feeHead.feeHeadSelectedOption.display.split("+"), payterms : feeHead.pmntTerms }; $http({ url : '<%= fileReader.getValue("ICOLLECT_SERVICE") %>?<%=ICollectConstants.FLOW_NAME_PARAM%>=<%= ICollectConstants.FLOW_NAME %>&<%=ICollectConstants.MERC_ID%>=<%= icMerchantMasterBean.getIcMerchantMasterId() %>&<%= ICollectConstants.BANK_ID%>=<%= icMerchantMasterBean.getIcBankMasterId()%>&req_id=validateFeeAndSendResponse&checksum=BFEDE97EF99CCF9C1362EBFE7C94E3233ED6E2B7637AC7BDB34AC916D9BAC3AD&reqtoken=<%=req_token%>'+'&method=TEST', method : "POST", params : { jsondataforST: btoa(angular.toJson(formData)), responseType:"1", backUrl :'<%= backUrl%>', icMerchantMasterId : <%= icMerchantMasterBean.getIcMerchantMasterId()%>, icMerchantBankId : <%= icMerchantMasterBean.getIcBankMasterId()%> } }).then(function Success(response){ if(response.data.statusCode=="100"){ var data = response.data; // 在这里执行所有需要用到data的逻辑 console.log("拿到后端数据了", data); $scope.separateTermLogic(feeHead, data); // 如果需要把数据绑定到scope供视图使用 $scope.selectedTermsData = data; } }, function error(response){ console.log("error occured", response); }); // 这里不需要return任何东西,因为逻辑都在回调里完成了 };
为什么原来的写法不行?
再简单总结下:JavaScript是单线程的,异步操作(比如HTTP请求)会被放到任务队列里,等到主线程的代码执行完才会执行回调。你的SelectedTerms函数里,$http发起请求后,主线程会继续执行到return data,这时候回调函数还没被执行,data还是初始的空字符串(或者undefined),所以返回值不符合预期。
内容的提问来源于stack exchange,提问作者Rohan Pandey

