Javascript 在服务中处理 $http 响应
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12505760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Processing $http response in service
提问by bsr
I recently posted a detailed description of the issue I am facing hereat SO. As I couldn't send an actual $http
request, I used timeout to simulate asynchronous behavior. Data binding from my model to view is working correct, with the help of @Gloopy
我最近公布的我面对这个问题的详细说明,这里的SO。由于无法发送实际$http
请求,我使用超时来模拟异步行为。在@Gloopy 的帮助下,从我的模型到视图的数据绑定工作正常
Now, when I use $http
instead of $timeout
(tested locally), I could see the asynchronous request was successful and data
is filled with json response in my service. But, my view is not updating.
现在,当我使用$http
而不是$timeout
(在本地测试)时,我可以看到异步请求成功并data
在我的服务中填充了 json 响应。但是,我的观点没有更新。
updated Plunkr here
在这里更新了 Plunkr
回答by Pete BD
Here is a Plunk that does what you want: http://plnkr.co/edit/TTlbSv?p=preview
这是一个可以满足您要求的 Plunk:http://plnkr.co/edit/TTlbSv?p=preview
The idea is that you work with promises directly and their "then" functions to manipulate and access the asynchronously returned responses.
这个想法是,您可以直接使用 Promise 及其“then”函数来操作和访问异步返回的响应。
app.factory('myService', function($http) {
var myService = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('test.json').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
// Return the promise to the controller
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
});
Here is a slightly more complicated version that caches the request so you only make it first time (http://plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview):
这是一个稍微复杂的版本,它缓存请求,因此您只需第一次创建(http://plnkr.co/edit/2yH1F4IMZlMS8QsV9rHv?p=preview):
app.factory('myService', function($http) {
var promise;
var myService = {
async: function() {
if ( !promise ) {
// $http returns a promise, which has a then function, which also returns a promise
promise = $http.get('test.json').then(function (response) {
// The then function here is an opportunity to modify the response
console.log(response);
// The return value gets picked up by the then in the controller.
return response.data;
});
}
// Return the promise to the controller
return promise;
}
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
$scope.clearData = function() {
$scope.data = {};
};
$scope.getData = function() {
// Call the async method and then do stuff with what is returned inside our own then function
myService.async().then(function(d) {
$scope.data = d;
});
};
});
回答by allenhwkim
Let it be simple. It's as simple as
让它简单点。就这么简单
- Return
promise
in your service(no need to usethen
in service) - Use
then
in your controller
promise
在您的服务中返回(无需then
在服务中使用)then
在您的控制器中使用
Demo. http://plnkr.co/edit/cbdG5p?p=preview
演示。http://plnkr.co/edit/cbdG5p?p=preview
var app = angular.module('plunker', []);
app.factory('myService', function($http) {
return {
async: function() {
return $http.get('test.json'); //1. this returns promise
}
};
});
app.controller('MainCtrl', function( myService,$scope) {
myService.async().then(function(d) { //2. so you can use .then()
$scope.data = d;
});
});
回答by Tosh
Because it is asynchronous, the $scope
is getting the data before the ajax call is complete.
因为是异步的,所以$scope
是在ajax调用完成之前获取数据。
You could use $q
in your service to create promise
and give it back to
controller, and controller obtain the result within then()
call against promise
.
您可以$q
在您的服务中使用创建promise
并将其返回给控制器,控制器在then()
调用中获取结果promise
。
In your service,
在您的服务中,
app.factory('myService', function($http, $q) {
var deffered = $q.defer();
var data = [];
var myService = {};
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data = d;
console.log(d);
deffered.resolve();
});
return deffered.promise;
};
myService.data = function() { return data; };
return myService;
});
Then, in your controller:
然后,在您的控制器中:
app.controller('MainCtrl', function( myService,$scope) {
myService.async().then(function() {
$scope.data = myService.data();
});
});
回答by Guillaume86
tosh shimayama have a solution but you can simplify a lot if you use the fact that $http returns promises and that promises can return a value:
tosh shimayama 有一个解决方案,但如果您使用 $http 返回承诺并且承诺可以返回一个值的事实,您可以简化很多:
app.factory('myService', function($http, $q) {
myService.async = function() {
return $http.get('test.json')
.then(function (response) {
var data = reponse.data;
console.log(data);
return data;
});
};
return myService;
});
app.controller('MainCtrl', function( myService,$scope) {
$scope.asyncData = myService.async();
$scope.$watch('asyncData', function(asyncData) {
if(angular.isDefined(asyncData)) {
// Do something with the returned data, angular handle promises fine, you don't have to reassign the value to the scope if you just want to use it with angular directives
}
});
});
A little demonstration in coffeescript: http://plunker.no.de/edit/ksnErx?live=preview
在 coffeescript 中的一个小演示:http://plunker.no.de/edit/ksnErx?live=preview
Your plunker updated with my method: http://plnkr.co/edit/mwSZGK?p=preview
你的plunker用我的方法更新:http://plnkr.co/edit/mwSZGK?p=preview
回答by HasanAboShally
A much better way I think would be something like this:
我认为更好的方法是这样的:
Service:
服务:
app.service('FruitsManager',function($q){
function getAllFruits(){
var deferred = $q.defer();
...
// somewhere here use: deferred.resolve(awesomeFruits);
...
return deferred.promise;
}
return{
getAllFruits:getAllFruits
}
});
And in the controller you can simply use:
在控制器中,您可以简单地使用:
$scope.fruits = FruitsManager.getAllFruits();
Angular will automatically put the resolved awesomeFruits
into the $scope.fruits
.
Angular 会自动将解析的awesomeFruits
放入$scope.fruits
.
回答by JhonQO
I had the same problem, but when I was surfing on the internet I understood that $http return back by default a promise, then I could use it with "then" after return the "data". look at the code:
我遇到了同样的问题,但是当我在互联网上冲浪时,我明白 $http 默认返回一个承诺,然后我可以在返回“数据”后将它与“then”一起使用。看代码:
app.service('myService', function($http) {
this.getData = function(){
var myResponseData = $http.get('test.json').then(function (response) {
console.log(response);.
return response.data;
});
return myResponseData;
}
});
app.controller('MainCtrl', function( myService, $scope) {
// Call the getData and set the response "data" in your scope.
myService.getData.then(function(myReponseData) {
$scope.data = myReponseData;
});
});
回答by Shadoweb
Related to this I went through a similar problem, but not with get or post made by Angular but with an extension made by a 3rd party (in my case Chrome Extension).
The problem that I faced is that the Chrome Extension won't return then()
so I was unable to do it the way in the solution above but the result is still Asynchronous.
So my solution is to create a service and to proceed to a callback
与此相关,我遇到了类似的问题,但不是使用 Angular 制作的 get 或 post,而是使用第三方制作的扩展程序(在我的情况下为 Chrome 扩展程序)。
我面临的问题是 Chrome 扩展程序不会返回,then()
因此我无法按照上述解决方案中的方式进行操作,但结果仍然是异步的。
所以我的解决方案是创建一个服务并进行回调
app.service('cookieInfoService', function() {
this.getInfo = function(callback) {
var model = {};
chrome.cookies.get({url:serverUrl, name:'userId'}, function (response) {
model.response= response;
callback(model);
});
};
});
Then in my controller
然后在我的控制器中
app.controller("MyCtrl", function ($scope, cookieInfoService) {
cookieInfoService.getInfo(function (info) {
console.log(info);
});
});
Hope this can help others getting the same issue.
希望这可以帮助其他人遇到同样的问题。
回答by Whisher
I've read http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/[AngularJS allows us to streamline our controller logic by placing a promise directly on the scope, rather than manually handing the resolved value in a success callback.]
我读过http://markdalgleish.com/2013/06/using-promises-in-angularjs-views/[AngularJS 允许我们通过直接在作用域上放置一个承诺来简化我们的控制器逻辑,而不是手动处理已解决的成功回调中的值。]
so simply and handy :)
如此简单方便:)
var app = angular.module('myApp', []);
app.factory('Data', function($http,$q) {
return {
getData : function(){
var deferred = $q.defer();
var promise = $http.get('./largeLoad').success(function (response) {
deferred.resolve(response);
});
// Return the promise to the controller
return deferred.promise;
}
}
});
app.controller('FetchCtrl',function($scope,Data){
$scope.items = Data.getData();
});
Hope this help
希望这有帮助
回答by Gloopy
When binding the UI to your array you'll want to make sure you update that same array directly by setting the length to 0 and pushing the data into the array.
将 UI 绑定到数组时,您需要确保通过将长度设置为 0 并将数据推送到数组中来直接更新同一个数组。
Instead of this (which set a different array reference to data
which your UI won't know about):
而不是这个(它设置了一个data
你的 UI 不知道的不同的数组引用):
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data = d;
});
};
try this:
尝试这个:
myService.async = function() {
$http.get('test.json')
.success(function (d) {
data.length = 0;
for(var i = 0; i < d.length; i++){
data.push(d[i]);
}
});
};
Here is a fiddlethat shows the difference between setting a new array vs emptying and adding to an existing one. I couldn't get your plnkr working but hopefully this works for you!
这是一个小提琴,显示了设置新数组与清空和添加到现有数组之间的区别。我无法让您的 plnkr 正常工作,但希望这对您有用!
回答by poshest
I really don't like the fact that, because of the "promise" way of doing things, the consumer of the service that uses $http has to "know" about how to unpack the response.
我真的不喜欢这样一个事实,因为“承诺”的做事方式,使用 $http 的服务的消费者必须“知道”如何解压响应。
I just want to call something and get the data out, similar to the old $scope.items = Data.getData();
way, which is now deprecated.
我只是想调用一些东西并获取数据,类似于现在已弃用的旧$scope.items = Data.getData();
方式。
I tried for a while and didn't come up with a perfect solution, but here's my best shot (Plunker). It may be useful to someone.
我尝试了一段时间并没有想出完美的解决方案,但这是我最好的尝试(Plunker)。它可能对某人有用。
app.factory('myService', function($http) {
var _data; // cache data rather than promise
var myService = {};
myService.getData = function(obj) {
if(!_data) {
$http.get('test.json').then(function(result){
_data = result.data;
console.log(_data); // prove that it executes once
angular.extend(obj, _data);
});
} else {
angular.extend(obj, _data);
}
};
return myService;
});
Then controller:
然后控制器:
app.controller('MainCtrl', function( myService,$scope) {
$scope.clearData = function() {
$scope.data = Object.create(null);
};
$scope.getData = function() {
$scope.clearData(); // also important: need to prepare input to getData as an object
myService.getData($scope.data); // **important bit** pass in object you want to augment
};
});
Flaws I can already spot are
我已经发现的缺陷是
- You have to pass in the object which you want the data added to, which isn't an intuitive or common pattern in Angular
getData
can only accept theobj
parameter in the form of an object (although it could also accept an array), which won't be a problem for many applications, but it's a sore limitation- You have to prepare the input object
$scope.data
with= {}
to make it an object (essentially what$scope.clearData()
does above), or= []
for an array, or it won't work (we're already having to assume something about what data is coming). I tried to do this preparation step INgetData
, but no luck.
- 您必须传入要将数据添加到的对象,这在 Angular 中不是直观或常见的模式
getData
只能接受obj
对象形式的参数(虽然也可以接受数组),这对很多应用来说不会是问题,但这是一个很痛的限制- 你必须准备输入对象
$scope.data
以= {}
使它的对象(基本上就是$scope.clearData()
上面的一样),或者= []
为一个数组,否则将无法工作(我们已经不得不承担一些关于什么样的数据来了)。我试着做这个准备步骤 INgetData
,但没有运气。
Nevertheless, it provides a pattern which removes controller "promise unwrap" boilerplate, and might be useful in cases when you want to use certain data obtained from $http in more than one place while keeping it DRY.
尽管如此,它提供了一种删除控制器“promise unwrap”样板的模式,并且在您想要在多个地方使用从 $http 获得的某些数据同时保持干燥的情况下可能很有用。