[RxJS + AngularJS] Sync Requests with RxJS and Angular

When you implement a search bar, the user can make several different queries in a row. With a Promise based implementation, the displayed result would be what the longest promise returns. This is the problem which we want to solve.

 

复制代码
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.rawgit.com/lodash/lodash/3.0.1/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/less.js/1.3.3/less.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.3.22/rx.all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
  <meta charset="utf-8">
  <title>JS Bin</title>
</head>
<body ng-app="APP">
  <div>
    <p>Click on "Pets" then on "Nothing" in each form</p>
    <p>Because pets' promise take long to resolve (longer than nothing's), it ends up with inconsistent output in Basic setup: nothing should be displayed but pets arrive!</p>
    <p>RxJs is an elegant way to prevent this concurrency problems from appearing.</p>
  </div>
  <div ng-controller="StandardController as standard" style="margin-top:50px;">
    <p>Basic setup</p>
    <input id="s-pets" name="standardEntityType" type="radio" ng-model="standard.filters.entityType" value="pets"> 
    <label for="s-pets">Pets</label>

    <input id="s-colors" name="standardEntityType" type="radio" ng-model="standard.filters.entityType" value="colors">
    <label for="s-colors">Colors</label>
    
    <input id="s-nothing" name="standardEntityType" type="radio" ng-model="standard.filters.entityType" value="nothing"> 
    <label for="s-nothing">Nothing</label>

    <p>
      {{ standard.filters | json }}
    </p>
    <ul>
      <li ng-repeat="entity in standard.entities">
        {{ entity.name }}
      </li>
    </ul>
  </div>
</body>
</html>
复制代码

 

复制代码
console.clear();

angular.module('APP', [])

.service('dataService', function($q, $timeout, $rootScope){
  var colors = [
    { name: 'red'   },
    { name: 'blue'  },
    { name: 'white' }
  ];
  
  var pets = [
    { name: 'cat' },
    { name: 'dog' }
  ];

  function fakeAjax(returnValue, delay){
    return $q(function(resolve){
      $timeout(function() {
        resolve(returnValue);
      }, delay);      
    });
  }
  
  function getColors(){
    return fakeAjax(colors, 1500);
  }

  function getPets(){
    return fakeAjax(pets, 3000);
  }
  
  function nullResponse(){
    return fakeAjax([], 1);
  }
  
  var mapping = {
    colors: getColors,
    pets:   getPets
  };
      
  return {
    getEntities: function(type){
      if (mapping[type])
        return mapping[type]();
      else
        return nullResponse();
    }
  };
})

.controller('StandardController', function($scope, dataService){
  this.entities = [];
  this.filters = {};
  var controller = this;
  
  function searchEntities(type){
    dataService.getEntities(type)
      .then(function(entities){
        controller.entities = entities;
      });
  }
  
  $scope.$watch(function(){ 
    return controller.filters.entityType;
  }, function(newVal){
    searchEntities(newVal);
  });
});
复制代码

 

 

Solution 1: Add lodash _.debounce method to add some delay.

复制代码
.controller('StandardController', function($scope, dataService){
  this.entities = [];
  this.filters = {};
  var controller = this;
  
  function _searchEntities(type){
    dataService.getEntities(type)
      .then(function(entities){
        controller.entities = entities;
      });
  }
  
  var searchEntities = _.debounce(_searchEntities, 1500);
  $scope.$watch(function(){ 
    return controller.filters.entityType;
  }, function(newVal){
    searchEntities(newVal);
  });
});
复制代码

 The problem here is we have to assume a correct time. Too long, and the UI is not responsible enough, and too short, and we may encounter weirdness again.

 

Solution 2: Using RxJS

复制代码
.controller('StandardController', function($scope, dataService){
  this.entities = [];
  this.filters = {};
  var controller = this;
  
  var Observable = Rx.Observable;
  var source = Observable.create(function(observe){
      $scope.$watch(function(){ 
        return controller.filters.entityType;
      }, function(newType){
        observe.onNext(newType);
      });
  }).flatMapLatest(function(type){
      return Observable.fromPromise(dataService.getEntities(type));
  });
  
  var sub = source.subscribe(function(entities){
    controller.entities = entities;
  });
});
复制代码

 No matter what order we click the radio buttons, we'll always get the expected outcome. RxJS will handle that for us. The main benefit of RxJS over mere promises is we always get the latest query results. You can see, this implementation of Rx has a way to cancel promises.

 

Two methos to apply:

复制代码
.controller('StandardController', function($scope, dataService){
  this.entities = [];
  this.filters = {};
  var controller = this;
  
  var Observable = Rx.Observable;
  var source = Observable.create(function(observe){
      $scope.$watch(function(){ 
        return controller.filters.entityType;
      }, function(newType){
        observe.onNext(newType);
      });
  }).debounce(500).flatMapLatest(function(type){
      return Observable.fromPromise(dataService.getEntities(type));
  });
  
  var sub = source.subscribe(function(entities){
    controller.entities = entities;
  });
  
  $scope.$on('destory', function(){
    sub.dispose();
  });

});
复制代码

The first thing is to clean after yourself. On each destroy event on the scope, so basically, whenever you use a router, it could be on route change, you have to dispose the listener so Rx knows it can get rid of everything linked to it.

The second thing is we don't want to put too much pressure on our server, so we are going to use debounce again. It's very important to understand here that debounce is not a way to avoid UI issues. It's a way to avoid useless server queries.

 

posted @   Zhentiw  阅读(1048)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示