Promise 与 RXJS的区别

回调地狱与 Promise

在使用 Ajax 的过程中,经常会遇到这种情况:我们需要在一个 Ajax 里面嵌套另一个 Ajax 调用,有时候甚至需要嵌套好几层 Ajax 调用,于是就形成了所谓的“回调地狱”:

enter image description here

这种代码最大的问题是可读性非常差,时间长了之后根本无法维护。

Promise 的出现主要就是为了解决这个问题,在 Promise 的场景下,我们可以这样写代码:

new Promise(function(resolve,reject){
    //异步操作之后用resolve返回data
})
.then(function(data){
    //依赖于Promise的第一个异步操作
})
.then(function(data){
    //依赖于Promise的第二个异步操作
})
.then(function(data){
    //依赖于Promise的第三个异步操作
})
.catch(function(reason){
    //处理异常
});

很明显,这样的代码可读性就强太多了,而且未来维护起来也很方便。

当然,Promise 的作用不止于此,如果你想更细致地研究 Promise,请看 MDN 上的这篇资料

RxJS 与 Promise 的共同点

RxJS 与 Promise 具有相似的地方,请看以下两个代码片段:

let promise = new Promise(resolve => {
    setTimeout(() => {
        resolve('---promise timeout---');
    }, 2000);
});
promise.then(value => console.log(value));
let stream1$ = new Observable(observer => {
    let timeout = setTimeout(() => {
        observer.next('observable timeout');
    }, 2000);

    return () => {
        clearTimeout(timeout);
    }
});
let disposable = stream1$.subscribe(value => console.log(value));

可以看到,RxJS 和 Promise 的基本用法非常类似,除了一些关键词不同。Promise 里面用的是 then() 和 resolve(),而 RxJS 里面用的是 next() 和 subscribe()。

RxJS 与 Promise 的3大重要不同点

任何一种技术或者框架,一定要有自己的特色,如果跟别人完全一样,解决的问题也和别人一样,那存在的意义和价值就会遭到质疑。

所以,RxJS 一定有和 Promise 不一样的地方,最重要的不同点有3个,请看下图:

enter image description here

依次给3块代码来示范一下:

let promise = new Promise(resolve => {
    setTimeout(() => {
        resolve('---promise timeout---');
    }, 2000);
});
promise.then(value => console.log(value));
let stream1$ = new Observable(observer => {
    let timeout = setTimeout(() => {
        observer.next('observable timeout');
    }, 2000);

    return () => {
        clearTimeout(timeout);
    }
});
let disposable = stream1$.subscribe(value => console.log(value));
setTimeout(() => {
    disposable.unsubscribe();
}, 1000);

从以上代码可以看到,Promise 的创建之后,动作是无法撤回的。Observable 不一样,动作可以通过 unsbscribe() 方法中途撤回,而且 Observable 在内部做了智能的处理,如果某个主题的订阅者为0,RxJS 将不会触发动作。

let stream2$ = new Observable(observer => {
    let count = 0;
    let interval = setInterval(() => {
        observer.next(count++);
    }, 1000);

    return () => {
        clearInterval(interval);
    }
});
stream2$.subscribe(value => console.log("Observable>"+value));

以上代码里面我们用 setInterval 每隔一秒钟触发一个新的值,源源不断,就像流水一样。

这一点 Promise 是做不到的,对于 Promise 来说,最终结果要么 resole(兑现)、要么 reject(拒绝),而且都只能触发一次。如果在同一个 Promise 对象上多次调用 resolve 方法,则会抛异常。而 Observable 不一样,它可以不断地触发下一个值,就像 next() 这个方法的名字所暗示的那样。

let stream2$ = new Observable(observer => {
    let count = 0;
    let interval = setInterval(() => {
        observer.next(count++);
    }, 1000);

    return () => {
        clearInterval(interval);
    }
});
stream2$
    .filter(val=>val%2==0)
    .subscribe(value => console.log("filter>"+value));
stream2$
    .map(value => value * value)
    .subscribe(value => console.log("map>"+value));

在上述代码里面,我们用到了两个工具函数:filter 和 map。

  • filter 的作用就如它的名字所示,可以对结果进行过滤,在以上代码里面,我们只对偶数值有兴趣,所以给 filter 传递了一个箭头函数,当这个函数的返回值为 true 的时候,结果就会留下来,其它值都会被过滤掉。
  • map 的作用是用来对集合进行遍历,比如例子里面的代码,我们把 Observable 返回的每个值都做了一次平方,然后再传递给监听函数。

类似这样的工具方法在 Observable 里面叫做 operator(操作符),所以有人说 Observable 就相当于异步领域的 Underscore 或者 lodash,这样的比喻是非常贴切的。这也是 Observable 比较强的地方,Promise 里面就没有提供这些工具函数。

Observable 里面提供了数百个这样的“操作符”,完整的列表和API文档请参考这里

我也看到有一些朋友在抱怨,说 RxJS 太过复杂,操作符的数量又特别多,不知道在什么场景下面应该用什么操作符。

实际上这种担心是多余的,因为在 RxJS 里面最常用的操作符不超过10个,不常用的操作符都可以在使用的时候再去查阅文档。

RxJS 和你自己开发的系统一样,常用的功能只有其中的20%,而剩余80%的功能可能永远不会被用到。所以,RxJS 并不像很多人说的那么玄乎,你一定能学会,我相信你。

RxJS 在 Angular 的典型应用场景1:http 服务

this.http
.get(url,{search:params})
.map((res:Response) => {
    let result=res.json();
    console.log(result);
    return result;
})
.catch((error:any) => Observable.throw(error || 'Server error'));

在新版本的 Angular 里面,http 服务的返回值都是 Observable 类型的对象,所以我们可以 subscribe(订阅)这个对象。当然,Observable 所提供的各种“操作符”都可以用在这个对象上面,比如上面这个例子就用到了 map 操作符。

RxJS 在 Angular 的典型应用场景2:事件处理

this.searchTextStream
.debounceTime(500)
.distinctUntilChanged()
.subscribe(searchText => {
    console.log(this.searchText);
    this.loadData(this.searchText)
});

这个例子里面最有意思的部分是 debounceTime 方法和 distinctUntilChanged 方法,这是一种“去抖动”效果。“去抖动”这个场景非常能体现 Observable 的优势所在,有一些朋友可能没遇到过这种场景,我来解释一下,以防万一。

在搜索引擎里面,我们经常会看到这样的效果:

enter image description here

这种东西叫做“动态搜索建议”,在用户敲击键盘的过程中,浏览器已经向后台发起了请求,返回了一些结果,目的是给用户提供一些建议。

效果看起来很简单,但是如果没有这个 debounceTime 工具函数,我们自己实现起来是非常麻烦的。这里的难点在于:用户敲击键盘的过程是源源不断的,我们并不知道用户什么时候才算输入完成。所以,如果让你自己来从零开始实现这种效果,你将会不得不使用定时器,不停地注册、取消,自己实现延时,还要对各种按键码做处理。

在 Observable 里面,处理这种情况非常简单,只要一个简单的 debounceTime 加 distinctUntilChanged 调用就可以了。

猜你喜欢

转载自blog.csdn.net/SunShinessx/article/details/88666179