几年前我就知道python和数据库了。
但我希望提高我有限的JavaScript知识。对于我的玩具项目,我想在Web浏览器中使用异步队列并使用AngularJS。
在python中有一个名为multiprocessing.Queue的好类,我过去曾使用它。
现在我搜索这样的东西,但是在AngularJS中
第1步:队列中拉出工作项(粉色圆圈)。只是一个视图json bytes。
第2步:用户处理数据。
第3步:出队关心将结果发送到服务器。
为什么这个"复杂"建立?因为我希望应用程序尽可能地响应。队列中应该预先加载一些数据,出队列应该处理响应通信。
另一个好处是,通过此设置,应用程序可以处理服务器或网络中断几分钟。
AngularJS的双向数据绑定立即更新用户编辑的数据并不适合我的问题。或者我错过了什么。我是AngularJS的新手。
图片中的粉色圆圈代表JSON数据结构。我想通过一个请求将每个请求推送到浏览器。
示例:
用户看到一个问题,然后他需要填写三个字段。例如:
数据应在使用后按下"提交"后放入队列。他应该立即得到下一个问题。
问题:
是否已为AngularJS提供生产者 - 消费者队列?如果没有,如何实施呢?
更新
从客户端发送数据可以使用普通的AJAX实现。预取数据的队列是更复杂的部分。虽然两者都可以使用相同的实现。客户端以超低延迟获取新数据非常重要。队列中每次最多应填充5个项目,以避免客户端等待数据。
在我的情况下,如果浏览器关闭并且队列中的项目丢失则无关紧要。填充队列在服务器部分是只读的。
我没有修复AngularJS。如果有充分的理由,我很乐意改变框架。
保留浏览器重新加载之间的队列可以使用localStorage(html5)
完成答案 0 :(得分:3)
重新思考,你真的需要在你的前端生产者 - 消费者吗?
在您的示例中,我认为在这种情况下,简单的pub-sub
或$Q
就足够了。
示例:
在用户提交问题的questionHandlerService
中,在您的示例question-submitted
中订阅订阅questionService
的订阅者服务,只需发布事件question-submitted
,数据,火灾和遗忘。您无需等待questionHandlerService
的响应。
请记住,javascript中只有一个主线程,如果你的方法会阻止ui,比如loop through 1000 items in the array, synchronous process them
,如果把它放在另一个“队列”中就没有帮助,因为它除非你在网络工作者中执行它,否则必须在执行时阻止ui。如果用户刷新浏览器会怎样?您的未处理请求刚刚丢失。
触发 XHR 调用不会阻止用户界面,在前端实现producer-consumer
没有意义,你只需验证输入就可以激活 XHR < / strong>,让后端处理繁重的工作,在后端控制器中,您可以使用队列立即保存请求和响应。并从任何线程或其他进程处理队列。
答案 1 :(得分:2)
Working Plunker - 后端服务仅用于模拟休息接口;我修正了一些bug,例如错误限制。所以,假设Plunker是最后一个版本......
我还没有足够的时间来改善以下内容,但是,您可以将其视为起点......
顺便说一句,我认为你需要的可能是:
angular
.module('myApp', ['Queue'])
.controller('MyAppCtrl', function($httpQueue, $scope) {
var vm = $scope;
// using a route.resolve could be better!
$httpQueue
.pull()
.then(function(tasks) { vm.tasks = tasks; })
.catch(function() { vm.tasks = [{ name: '', description: '' }]; })
;
vm.onTaskEdited = function(event, task, form) {
event.preventDefault();
if(form.$invalid || form.$pristine ) { return; }
return $httpQueue.push(task);
};
})
;
&#13;
<article ng-app="myApp">
<div ng-controller="MyAppCtrl">
<form ng-repeat="task in tasks" name="taskForm" ng-submit="onTaskEdited($event, task, taskForm)">
<input ng-model="task.name" placeholder="Task Name" />
<textarea ng-model="task.description"></textarea>
</form>
</div>
</article>
&#13;
(function(window, angular, APP) {
'use strict';
function $httpQueueFactory($q, $http) {
var self = this;
var api = '/api/v1/tasks';
self.queue = [];
var processing = false;
//Assume it as a private method, never call it directly
self._each = function() {
var configs = { cache: false };
return self
.isQueueEmpty()
.then(function(count) {
processing = false;
return count;
})
.catch(function() {
if(processing) {
return;
}
processing = true;
var payload = self.queue.shift();
var route = api;
var task = 'post';
if(payload.id) {
task = 'put';
route = api + '/' + payload.id;
}
return $http
[task](route, payload, configs)
.catch(function(error) {
console.error('$httpQueue._each:error', error, payload);
//because of the error we re-append this task to the queue;
return self.push(payload);
})
.finally(function() {
processing = false;
return self._each();
})
;
})
;
};
self.isQueueEmpty = function() {
var length = self.queue.length;
var task = length > 0 ? 'reject' : 'when';
return $q[task](length);
};
self.push = function(data) {
self.queue.push(data);
self._each();
return self;
};
self.pull = function(params) {
var configs = { cache: false };
configs.params = angular.extend({}, params || {});
return $http
.get(api, configs)
.then(function(result) {
console.info('$httpQueue.pull:success', result);
return result.data;
})
.catch(function(error) {
console.error('$httpQueue.pull:error', error);
return $q.reject(error);
})
;
};
}
APP
.service('$httpQueue', ['$q', '$http', $httpQueueFactory])
;
})(window, window.angular, window.angular.module('Queue', []));
&#13;
处理数据层的更改(你在队列中调用的)是一项更困难的任务,因为我们需要与当前的拉动保持同步最后一次拉 ...
顺便说一下,如果您正在寻找具有实时通知的系统,您可能应该查看套接字层 ...我建议 Socket.io < / strong>因为是经过良好测试,行业认可的解决方案。
如果您无法实现套接字层,则另一个解决方案可能是长轮询实施,详细说明here。
为简单起见,在这篇文章中,我们将实现一个更新当前任务列表的简单间隔... 所以,前面的例子就是:
angular
.module('myApp', ['Queue'])
.controller('MyAppCtrl', function($httpQueue, $scope, $interval) {
var vm = $scope;
var
pollingCount = 0, // infinite polling
pollingDelay = 1000
;
// using a route.resolve could be better!
$httpQueue
.pull()
.then(function(tasks) { vm.tasks = tasks; })
.catch(function() { vm.tasks = [{ name: '', description: '' }]; })
.finally(function() { return $interval(vm.updateViewModel.bind(vm), pollingDelay, pollingCount, true); })
;
var isLastPullFinished = false;
vm.updateViewModel = function() {
if(!isLastPullFinished) { return; }
return $http
.pull()
.then(function(tasks) {
for(var i = 0, len = tasks.length; i < len; i++) {
for(var j = 0, jLen = vm.tasks.length; j < jLen; j++) {
if(tasks[i].id !== vm.tasks[j].id) { continue; }
// todo: manage recursively merging, in angular 1.3+ there is a
// merge method https://docs.angularjs.org/api/ng/function/angular.merge
// todo: control if the task model is $dirty (if the user is editing it)
angular.extend(vm.tasks[j], tasks[i]);
}
};
return vm.tasks;
})
.finally(function() {
isLastPullfinished = true;
})
;
};
vm.onTaskEdited = function(event, task, form) {
event.preventDefault();
if(form.$invalid || form.$pristine ) { return; }
return $httpQueue.push(task);
};
})
;
&#13;
希望它有所帮助; 我没有对它进行测试,因此可能存在一些错误!
答案 2 :(得分:1)
我会查看承诺,因为它们提供了您似乎需要的异步功能,可能会在初始时提取所有或部分问题。如果您确实需要离线查看服务工作者,尽管他们仅限于较新的浏览器
如果您无法使用服务工作者,您可以创建一个服务(或工厂)来保存您的所有问题或稍微提前阅读。您可以使用$ http服务尝试并提取更多答案,如果您无法使用所拥有的内容,直到再次出现网络连接。
通过尝试在$ interval的循环中获取新答案(并发布答案),可以使用$ http服务来检查网络。
答案 3 :(得分:1)
申请结构: 2个数组,一个用另一个问题答案。
应用程序启动: 两个$ interval对象,一个从服务器获得前10个问题。如果问题缓冲区长度是&lt; 10它会将新问题推入阵列。
另一个函数检查答案数组,如果通信可用,则将结果发送给服务器。
当用户检查问题时,它会弹出第一个数组并推入答案数组。
这就是全部..角度2方式数据绑定使用应用程序中的函数调用显示最老的问题,如giveMeTheLatestQuestion ...
希望它有所帮助!