利用ajax实现服务端异步任务状态监听

拼搏现实的明天。 2022-08-09 06:53 293阅读 0赞

项目中为了改善用户体验,一些耗时操作常常会使用异步方式执行,但是一旦将任务放入后台,用户就不能及时得到任务执行的结果。所以需要涉及一种方式能够持续跟踪任务的进行情况。每个监听任务会被分配一个任务ID,在任务监听结束后被释放。

全局监听器的实现

基于单例模式设置一个HashMap能被系统全局访问用于存放任意任务的监听数据。同时Value部分使用特殊特殊结构。

结构设计

即HashMap中的Value存储的部分。

  1. public class ValueStruc {
  2. private boolean expireable;
  3. private Date expireTime;
  4. private Object value;
  5. private int readCount;
  6. public ValueStruc(){}
  7. public ValueStruc(Object value, Date expireTime){
  8. this.value = value;
  9. this.expireable = true;
  10. this.expireTime = expireTime;
  11. this.readCount = 0;
  12. }
  13. public ValueStruc(Object value){
  14. this.value = value;
  15. this.readCount = 0;
  16. }
  17. public Object readValue(){
  18. readCount = readCount +1;
  19. return this.value;
  20. }
  21. public boolean expired(){
  22. if(expireable){
  23. return new Date().after(expireTime);
  24. } else {
  25. return false;
  26. }
  27. }
  28. public boolean isExpireable(){
  29. return this.expireable;
  30. }
  31. public void setExpireable(boolean exb){
  32. this.expireable = exb;
  33. }
  34. public Date getExpireTime(){
  35. return expireTime;
  36. }
  37. public void setExpireTime(Date exd){
  38. this.expireTime = exd;
  39. }
  40. public Object getValue(){
  41. return value;
  42. }
  43. public void setValue(Object v){
  44. this.value = v;
  45. }
  46. public int getReadCount(){
  47. return readCount;
  48. }
  49. public void setReadCount(int readCount){
  50. this.readCount = readCount;
  51. }
  52. }

监听器

用于在程序的任意位置注册监听ID,更新、读取ID对应的信息。同时应该支持ID的过期清除,读取行为记录等。
代码如下:

  1. public class DataObserver {
  2. private HashMap<String, ValueStruc> map;
  3. private volatile static DataObserver instance;
  4. private DataObserver(){
  5. map = new HashMap<String, ValueStruc>();
  6. }
  7. public static DataObserver getInstance(){
  8. if(instance == null){
  9. synchronized(DataObserver.class){
  10. if(instance == null){
  11. instance = new DataObserver();
  12. }
  13. }
  14. }
  15. return instance;
  16. }
  17. /** * 注册Key<br> * 基本:输入键值对,key, value<br> * 可选:如果需要有有效时间,则加入秒数,否则写入负数即可表示为不设置过期时间,0没有意义<br> * Function: register * * @author wengshengyuan DateTime 2015-10-12 上午9:16:01 * @param key * @param value * @param Sec * @return ValueStruc,已存在该key则返回null */
  18. public ValueStruc register(String key, Object value, int Sec) {
  19. if(map.containsKey(key))
  20. return null;
  21. Date now = new Date();
  22. if(Sec > 0){
  23. Date expireTime = DateUtils.addSeconds(now, Sec);
  24. ValueStruc v = new ValueStruc(value, expireTime);
  25. map.put(key, v);
  26. return v;
  27. } else {
  28. ValueStruc v = new ValueStruc(value);
  29. map.put(key, v);
  30. return v;
  31. }
  32. }
  33. /** * 更新已存在的值 * Function: update * * @author wengshengyuan DateTime 2015-10-12 下午1:45:46 * @param key * @param value * @return 返回新的值,若没有该Key, 返回null */
  34. public ValueStruc update(String key, Object value){
  35. if(map.containsKey(key)){
  36. ValueStruc v = map.get(key);
  37. v.setValue(value);
  38. map.put(key, v);
  39. return v;
  40. } else {
  41. return null;
  42. }
  43. }
  44. /** * 读取key<br> * Function: read * * @author wengshengyuan DateTime 2015-10-12 上午9:29:48 * @param key * @return 写入的Object, 过期或者没有均返回null */
  45. public Object read(String key){
  46. ValueStruc v = map.get(key);
  47. if(v == null)
  48. return null;
  49. if(v.expired()){
  50. map.remove(key);
  51. return null;
  52. }
  53. return v.readValue();
  54. }
  55. /** * 删除某一Key的值<br> * 若删除成功,则返回之前的值,若删除不成功,则返回null * Function: remove * * @author wengshengyuan DateTime 2015-10-12 上午11:09:39 * @param key * @return */
  56. public ValueStruc remove(String key){
  57. return map.remove(key);
  58. }
  59. /** * 清空map * Function: clear * * @author wengshengyuan DateTime 2015-10-12 上午11:10:23 */
  60. public void clear(){
  61. map = new HashMap<String, ValueStruc>();
  62. }
  63. }

使用方式

注册

在执行异步方法前,生成监听ID, 并传入异步执行体。执行状态的更新由异步任务更新。

  1. String observerID = "taskName_"+UUIDUtils.getUUID32();
  2. DataObserver.getInstance().register(observerID,value,-1);
  3. AsyncTask task = new AsyncTask(String observerID);
  4. task.start();

更新

在异步任务中随时调用

  1. DataObserver.getInstance().update(observerID, value);

读取

  1. DataObserver.getInstance().read(observerID);

Controller层接口

JAVA服务端API实现

通过Controller定义数据传输接口。包括:增、删、改、查和清除等功能。这里以查询为例。
Controller代码如下:

  1. @Controller
  2. @RequestMapping(value="/api/observer")
  3. public class DataObserverAPI {
  4. @ResponseBody
  5. @RequestMapping(value = "observe/{key}", method = RequestMethod.GET)
  6. public ResultInfo observe(HttpServletRequest request, @PathVariable("key") String key){
  7. ResultInfo result = new ResultInfo();
  8. Object o = DataObserver.getInstance().read(key);
  9. if(o == null){
  10. result.setStateId(-1);
  11. result.setErrorMsg("未查询到信息");
  12. } else {
  13. result.addObj2Map("value", o);
  14. }
  15. return result;
  16. }
  17. @ResponseBody
  18. @RequestMapping(value = "regester/{key}/{value}/{timeout}", method = RequestMethod.GET)
  19. public ResultInfo testObserver(HttpServletRequest request, @PathVariable("key") String key, @PathVariable("value") String value
  20. , @PathVariable("timeout") int timeout){
  21. ResultInfo result = new ResultInfo();
  22. ValueStruc v = DataObserver.getInstance().regester(key, value, timeout);
  23. if(v == null){
  24. result.setStateId(-1);
  25. result.setErrorMsg("插入失败");
  26. } else {
  27. result.addObj2Map("value", v);
  28. }
  29. return result;
  30. }
  31. @ResponseBody
  32. @RequestMapping(value = "update/{key}/{value}", method = RequestMethod.GET)
  33. public ResultInfo update(HttpServletRequest request, @PathVariable("key") String key, @PathVariable("value") String value){
  34. ResultInfo result = new ResultInfo();
  35. ValueStruc v = DataObserver.getInstance().update(key, value);
  36. if(v == null){
  37. result.setStateId(-1);
  38. result.setErrorMsg("更新失败");
  39. } else {
  40. result.addObj2Map("value", v);
  41. }
  42. return result;
  43. }
  44. @ResponseBody
  45. @RequestMapping(value="remove/{key}", method = RequestMethod.GET)
  46. public ResultInfo clear(HttpServletRequest request, @PathVariable("key") String key){
  47. ResultInfo result = new ResultInfo();
  48. ValueStruc v = DataObserver.getInstance().remove(key);
  49. result.addObj2Map("value", v);
  50. return result;
  51. }
  52. @ResponseBody
  53. @RequestMapping(value = "clear", method = RequestMethod.GET)
  54. public void clear(){
  55. DataObserver.getInstance().clear();
  56. }
  57. }

前端js实现

以通过接口调用服务器执行ping命令,并监听ping的结果为例,js代码如下(以下结合AngularJs,其他脚本亦类似):

第一步:调用异步服务并获取监听ID

通过约定,在调用异步任务发起后返回注册的监听ID。

  1. //调用服务
  2. this.ping = function($scope){
  3. var url = ctx + 'godmode/ping';
  4. var data = {
  5. ip : $scope.pingIP
  6. };
  7. $.ajax({
  8. type:"post",
  9. url:url,
  10. async:false,
  11. data :data,
  12. success: function(r){
  13. console.log(r);
  14. $scope.observerID = r.map.observerID[0];
  15. },
  16. error:function(){
  17. $interva
  18. }
  19. });
  20. }

第二步:监听ID数据

  1. //定时调用监听
  2. var timer = $interval(function(){
  3. myService.observePing($scope,$interval);
  4. },1000);
  5. //监听服务
  6. this.observePing = function($scope,$interval){
  7. console.log('observing:'+$scope.observerID);
  8. $.ajax({
  9. type : "get",
  10. url: ctx + "api/observer/observe/"+$scope.observerID,
  11. dataType: "json",
  12. async: true,
  13. timeout: 10000,
  14. error: function(r) {
  15. console.log('canceling observer');
  16. $interval.cancel(timer);
  17. },
  18. success: function(r) {
  19. if (r.stateId == 0) {
  20. var text = "";
  21. $.each(r.map.value[0], function(index, item) {
  22. text = text + item + "\n";
  23. });
  24. $scope.pingResults = text;
  25. } else {
  26. //监听资源释放后,停止interval
  27. console.log('canceling observer');
  28. $interval.cancel(timer);
  29. }
  30. }
  31. });
  32. }

发表评论

表情:
评论列表 (有 0 条评论,293人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ajax异步下载文件并判断状态

    今日需要对公司的代码进行更新,发现之前有的文件丢失导致下载不到直接跳转到一个空的页面,第一反应是应该没有用异步请求,直接用a标签或者表单之类的处理下载文件请求了。 但是...