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.

[RxJS + AngularJS] Sync Requests with RxJS and Angular的更多相关文章

  1. Angular Multiple HTTP Requests with RxJS

    原文:https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs ----------------------------- ...

  2. [Vue-rx] Cache Remote Data Requests with RxJS and Vue.js

    A Promise invokes a function which stores a value that will be passed to a callback. So when you wra ...

  3. [AngularJS] Consistency between ui-router states and Angular directives

    ui-router's states and AngularJS directives have much in common. Let's explores the similarities bet ...

  4. [AngularJS 2 实践 一]My First Angular App

    最近一直在看关于AngularJS 2的资料,查看了网上和官网很多资料,接下来就根据官网教程步骤一步步搭建我的第一个Angular App AngularJS 2说明请参考:http://cnodej ...

  5. [RxJS] Refactoring Composable Streams in RxJS, switchMap()

    Refactoring streams in RxJS is mostly moving pieces of smaller streams around. This lessons demonstr ...

  6. [RxJS] Reactive Programming - Why choose RxJS?

    RxJS is super when dealing with the dynamic value. Let's see an example which not using RxJS: var a ...

  7. AngularJS进阶(二十五)requirejs + angular + angular-route 浅谈HTML5单页面架构

    requirejs + angular + angular-route 浅谈HTML5单页面架构 众所周知,现在移动Webapp越来越多,例如天猫.京东.国美这些都是很好的例子.而在Webapp中,又 ...

  8. angularjs探秘<三> 控制器controller及angular项目结构

    先来看一个例子 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset=&quo ...

  9. AngularJS源码解析1:angular自启动过程

    angularJS加载进来后,会有一个立即执行函数调用,在源代码的最下面是angular初始化的地方.代码展示: bindJQuery(); publishExternalAPI(angular); ...

随机推荐

  1. 浅谈购物车中cookie的使用

    购物车对于电商网站来说是一个非常重要的模块.最近自己的项目中也用到了,所以拿出来说说事! 购物车是用户选择商品的一个缓存的地方.其中包含了商品的基本信息,例如:商品的描述,商品的价格,商品的数量等等. ...

  2. PYTHON开发--面向对象基础二

    一.成员修饰符 共有成员 私有成员, __字段名 - 无法直接访问,只能间接访问 1.     私有成员 1.1  普通方法种的私有成员 class Foo: def __init__(self, n ...

  3. Python 手册——开胃菜

    如果你写过大规模的Shell脚本,应该会有过这样的体会:你还非常想再加一些别的功能进去,但它已经太大. 太慢.太复杂了:或者这个功能需要调用一个系统函数,或者它只适合通过C来调用……通常这些问题还不足 ...

  4. linux安装python使用的MySQLdb

    安装mysqldb模块需已安装mysql 使用pip安装MySQLdb pip install mysql-python mac os安装mysqldb sudo pip install mysql- ...

  5. iOS: 学习笔记, swift扩展

    // // YYExtension.swift // // Created by yao_yu on 14-7-18. // Copyright (c) 2014年 yao_yu. All right ...

  6. linux之GDB常用命令汇总

    查看gdb的版本号 (1)rpm -q gdb 会显示是否安装gdb及版本号 (2)gdb --version也可以 breakpoint b main; b 20; 设置断点 breakpoint ...

  7. FutureTask源码解读

    import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.ut ...

  8. 小记,取GB2312汉字的首字母【转】

    /// <summary> /// PY 的摘要说明. /// </summary> public class PY { // Fields private string m_ ...

  9. struts中如何将前台的值能在action中获取到

    如何获取值----三种方式(属性驱动,对象驱动,模型驱动)  A:属性驱动 必须生成get,set方法  B:对象驱动 给对象也必须生成get,set方法  c模型驱动 模型驱动需要action去实现 ...

  10. Distinct Substrings

    spoj694:http://www.spoj.com/problems/DISUBSTR/ 题意:给以一个串,求这个串的所有不同子串的个数. 题解:第一次接触后缀数组,这里可以转化成,求所有子串后缀 ...