我在设置将执行异步http请求的服务时遇到问题。
背景
我有一个后端端点/cars.json
当您第一次点击此端点时,将获取将获取所有汽车信息的后台作业。然后,服务器返回状态为200的响应:
{"status":"started", "percentage_completion":0, "data":null, "error":null}
对此终端的连续请求将继续返回状态200并更新 percentage_completion
{"status":"started", "percentage_completion":45, "data":null, "error":null}
{"status":"started", "percentage_completion":90, "data":null, "error":null}
最后,对端点的请求将返回:
{"status":"finished", "percentage_completion":100, "data": {...}, "error":null}
和欢乐日子我有数据,我可以将其添加到$scope
。
但我很难绕过承诺和有条不紊的做事方式......
我飙了这样的东西:
var dashboard = angular.module('dashboard', [
'templates'
]);
dashboard.controller('dashboardController', ['carsService', '$scope',
function(carsService, $scope){
$scope.test = "Loading...";
carsService.get()
.then(function(response){
$scope.test = response.data;
});
}]
);
dashboard.factory('carsService', ['$http', function($http){
var get = function(){
return $http.get('/cars.json');
};
return {
get: get
};
}]);
它向服务器发送单个请求,并使用表明作业已启动的第一个响应更新范围上的测试。当我几秒钟后刷新页面时,我会使用正确的数据和完成状态更新测试。
什么是最好的方式,角度方式:)与承诺和东西让服务和控制器自动完成。
我头脑中的第一件事是使用$interval
或类似的东西,并定期从.then(成功)重新发送请求,但也许有更好的方法。
Angular文档说明了进度/通知承诺,但目前我还不知道如何连接它。
显然我目前还没有完全理解这些承诺,我刚刚介绍了它们 - 你能否分享一些关于如何使用angular来处理异步请求的技巧/资源?
答案 0 :(得分:4)
我并不认为我完全理解你的问题,但是如果你想要在他们回来时获得进展更新,Angular会在他们的承诺服务中建立一个进度回调(正如你所提到的)。您.then()
内的第一个回调是您的成功回调,第二个是您的错误回调,第三个是您的进度回调。
因此,当您将回调链接到承诺时,您可以使用以下语法更新进度指示器:
carsService.get()
.then(function(response){
// success
$scope.test = response.data;
$scope.$apply(); // or wrapped in $timeout
}, function(error){
// error
}, function(percentComplete){
// progress
$scope.progress = percentComplete;
$scope.$apply(); // or wrapped in $timeout
});
至于在编写和链接你的承诺时使用的最佳语法,John Papa有一个非常棒(并且非常深入)的风格指南,可以帮助你更好地组织你的Angular代码。你可以找到这个here。
此处的示例jsfiddle也展示了此通知/进度回调的实际效果。它使用超时来模拟异步调用,但很好地显示了一般的想法。
还要记住,当您进行这些异步调用时,您可能需要运行$scope.$apply()
或将回调中的代码包装在$timeout(function(){ //update data });
内更新范围数据的回调中,以便运行Angular再次消化周期(异步调用在Angular的内置观察者之外更新您的数据)。这将导致您的视图更新,而无需重新加载页面。有很多关于此问题的博客帖子和SO问题,如果您以前从未遇到过这种行为,可以使用它们。这里的one很好地涵盖了它。
答案 1 :(得分:2)
我猜你可以使用嵌套的promise实现进度通知:
function get(){
var defer = $q.defer();
(function fetchData(){
$http.get('/cars.json').then(function(result){
if(result.status === 'started'){
fetchData();
defer.notify(result);
}else if(result.status === 'finished'){
defer.resolve(result);
}
}, function(err){
defer.reject(err);
})
})()
return defer.promise;
}
当您致电get()
时,它首先致电fetchData
以从服务器请求数据。解决fetchData
后,检查result.status
,如果状态尚未完成,请再次致电fetchData
并通知outter延迟,否则,解决最终结果的延迟
//in controller
carService.get().then(
function(finalData){ $scope.test = finalData },
function(err){ ... },
function(notifyData){ $scope.test = notifyData }
}
答案 2 :(得分:1)
这里有关于AngularJS中$ http服务的参考。
https://docs.angularjs.org/api/ng/service/$http
这是关于$ http服务的解释以及如何使用then() 功能
select sales.id, sales.refid, reference.refname, sales.invoice, sales.itemid, sales.price as sellprice, purchase.price as buyprice, sales.price - purchase.price as profit from sales left outer join reference on reference.refid = sales.refid left outer join purchase on purchase.itemid = sales.itemid
最好的方法是使用HTML5 Server-Sent Events或WebSockets技术,您可以实时获取数据。
在这个演示中,我在then()函数中使用了匿名函数。
我使用PHP,Server Sent Events,JSON和AngularJS Factory服务制作了两个非常简单的演示。
AngularJS工厂服务和PHP JSON响应: cars.php
// Simple GET request example:
$http({
method: 'GET',
url: '/someUrl'
}).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
<?php
header("Access-Control-Allow-origin: *"); // To allow cross-origin HTTP request.
header("Content-Type: application/json"); // JSON response.
header("Cache-Control: no-cache"); // No cache.
/*
Only to display random numeric values.
In a real scenario the data can be obtained from database.
*/
$min = 0;
$max = 100;
$count = rand($min, $max); // Get a random numeric value.
$status = "started";
if($count == $max)
{
$status = "finished"; // If the $count == 100 then finished.
}
// Builing an array with current data.
$array = array("status" => $status, "percentage_completion" => $count, "data" => null, "error" => null);
echo json_encode($array); // Encode the array to the json representation.
?>
(function() {
var dashboard = angular.module("dashboard", []);
dashboard.controller("dashboardController", ["carsService", "$scope",
function(carsService, $scope) {
$scope.statusJSON = "Loading...";
$scope.testJSON = {};
$scope.initJSON = function() {
(function loop() { // Looping function to continous request.
carsService.get().then(function(response) { // Angular promise by using then() function.
$scope.testJSON = response.data;
$scope.statusJSON = $scope.testJSON.status;
console.log($scope.testJSON);
}, function(response) {
console.log("Error: " + response.status);
});
setTimeout(loop, 1000); // Call the $http service every 1 second.
})();
};
}
]);
dashboard.factory("carsService", ["$http",
function($http) {
return { // This factory service returns an object with the get function.
get: function() {
return $http.get("http://dfjb.webcindario.com/cars.php", {
responseType: "json"
}); // Returns a $http service.
}
};
}
]);
})();
.json {
border: solid 1px #444444;
margin: 5px;
padding: 5px;
}
HTML5服务器发送事件:data.php
<html data-ng-app="dashboard">
<head>
<title>Demo AngularJS</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body data-ng-controller="dashboardController">
<div class="json" data-ng-init="initJSON()">
<h3>Demo With Angular $http Service</h3>
<div data-ng-bind="statusJSON"></div>
percentage_completion: {{testJSON.percentage_completion}}
<br />
<progress min="0" max="100" value="{{testJSON.percentage_completion}}" />
</div>
</body>
</html>
<?php
header("Access-Control-Allow-origin: *"); // To allow cross-origin HTTP request.
header("Content-Type: text/event-stream"); // To send event streams.
header("Cache-Control: no-cache"); // No cache.
/*
Only to display random numeric values.
In a real scenario the data can be obtained from database.
*/
$min = 0;
$max = 100;
$count = rand($min, $max); // Get a random numeric value.
$status = "started";
if($count == $max)
{
$status = "finished"; // If the $count == 100 then finished.
}
// Builing an array with current data.
$array = array("status" => $status, "percentage_completion" => $count, "data" => null, "error" => null);
echo "data: ". json_encode($array) ."\n\n"; // Encode the array to the json representation. The event-stream always start with "data: ".
flush(); // Flush the output data back to the web page.
?>
(function() {
var dashboard = angular.module("dashboard", []);
dashboard.controller("dashboardController", ["carsService", "$scope",
function(carsService, $scope) {
$scope.statusSSE = "Loading...";
$scope.testSSE = {};
$scope.initSSE = function() {
// Check if EventSource is supported by the browser.
if (typeof(EventSource) !== "undefined") {
carsService.getDataSSE().onmessage = function() {
// The factory service has a function that returns an EventSource object, so this can be accessed in the controller.
$scope.testSSE = JSON.parse(event.data); // Parse the string in an object.
$scope.statusSSE = $scope.testSSE.status;
$scope.$apply(); // Update the $scope variable so can be used in the view.
console.log($scope.testSSE);
};
} else {
alert("SSE not supported by browser.");
}
};
}
]);
dashboard.factory("carsService", [
function() {
return {
getDataSSE: function() {
// HTML5 Server-Sent Events Implementation.
return new EventSource("http://dfjb.webcindario.com/data.php"); // Returns the EventSource object.
}
};
}
]);
})();
.sse {
border: solid 1px #FF44AA;
margin: 5px;
padding: 5px;
}
最后,在本演示中,我将向您展示如何使用PHP中的AngularJS Factory服务和使用json响应的HTML5 Server-Sent Events Technology来实现服务器的实时通知。
如果您检查控制台,则会自动在EventStream选项卡中获得一个json字符串。
答案 3 :(得分:1)
所有的功劳都归功于MarkoCen关于使用链式承诺的建议。这是我的最终解决方案:
app.js.coffee
angular
.module 'dashboard', ['templates']
car_service.coffee
CarService = ($q, $http)->
get = (url,defer)->
$http.get(url).then(
(result)->
data = result.data
if data.status == "started"
get(url, defer)
defer.notify(data.percentage_completion)
else
defer.resolve(data.data.cars)
)
defer.promise
return{
getAll: (url)->
defer = $q.defer()
get(url,defer)
}
angular
.module 'dashboard'
.factory 'CarService', [
'$q'
'$http'
CarService
]
cars_controller.coffee
DashboardController = (CarService) ->
vm = @
vm.campaigns = []
vm.statusBar = {}
CarService.getAll('/cars.json').then(
(data)->
vm.campaigns = data
(reason)->
console.log reason
(update)->
vm.statusBar.percentage = update
)
return
angular
.module 'dashboard'
.controller 'DashboardController', [
'CarService'
DashboardController
]