阅读 159

RxJS Observables vs Promise 之简单对比

最近在学习RxJS,它是使用 Observables 的响应式编程的库,它使编写异步或基于回调的代码更容易。

下面主要介绍Observables 与 promise的不同点。

单值与多值

const numberPromise = new Promise((resolve) => {
    resolve(5);
    resolve(10)
});

numberPromise.then(value => console.log(value));

// 输出 只会有 5
复制代码

下面改写为observables的写法,使用 next 替代 promise 的 resolve, 用subscribe 取代then来订阅结果。

const Observable = require('rxjs/Observable').Observable;

const numberObservable = new Observable((observer) => {
    observer.next(5);
    observer.next(10);
});

numberObservable.subscribe(value => console.log(value));

// 输出 5 10
复制代码

observable是可以连续订阅的,这个和promise的区别很大。平时我们遇到的可能大多数都是一个请求一个响应的这种情况,但是我们也会存在一些情况:

  • setInterval,需要resolve多个值
  • webSockets
  • DOM events
const numberObservable = new Observable((observer) => {
      let i = 0;
      setInterval(() => {
          observer.next(i++);
      }, 1000);
});
    
numberObservable.subscribe(value => console.log(value));    
// 输出 0 1 2 3 4 5
复制代码

代码执行顺序

const promise = new Promise((resolve) => {
    console.log('promise call')
    resolve(1);
    console.log('promise end')
})

// 执行这段代码 promise call 和 promise end 会立即执行
const observable = new Observable(() => {
    console.log('I was called!');
});

// 此时并没有console

// 只有 observable.subscribe(); 这个时候 I was called!才会被打印出来。
复制代码

上面两段代码就对比可以发现Observables是lazy的,只有当有人去订阅(subscribe)的时候Observables才会真正的被执行。

如果上方setInterval的函数写在promise里面,但是没有promise.then之类的函数就会造成资源的浪费,而在observable里面,不订阅连内存都不会分配。

不能取消 & 能取消

promise默认是不能取消的,可以使用promise的实现库 bluebird 来实现。bluebird是完全兼容promise并且添加了一些有用的方法。

const Observable = require('rxjs/Observable').Observable;

const observable = new Observable((observer) => {
    let i = 0;
    const token = setInterval(() => {
        observer.next(i++);
    }, 1000);
  
    return () => clearInterval(token);
});

const subscription = observable.subscribe(value => console.log(value + '!'));

setTimeout(() => {
    subscription.unsubscribe();
}, 5000)

// 结果

0!
1!
2!
3!
复制代码

这个地方需要注意的是, subscribe 返回的不是一个Observable! 这就是说不能和promise一样链式的subscribe。subscribe返回的是一个对于指定observable的 Subscription。他只有一个方法可以调用,就是unsubscribe。

单个订阅&多个订阅

promise 是比较激进的,在一个promise被创建的时候,他就已经执行了,并且不能重复的被执行了。

let time;
const waitOneSecondPromise = new Promise((resolve) => {
    console.log('promise call')
    time = new Date().getTime();
    setTimeout(() => resolve('hello world'), 1000);
});

waitOneSecondPromise.then((value) => {console.log( '第一次', value, new Date().getTime() - time)});

setTimeout(() => {
    waitOneSecondPromise.then((value) => {console.log('第二次', value, new Date().getTime() - time)});   
}, 5000)

// 输出结果是 promise call
第一次 hello world 1007
第二次 hello world 5006
复制代码

上面这个例子中,我创建了一个promise,他是立即执行的setTimeout,所以在第一个then函数中打印时间间隔是约等于 1s,这个是符合我们预期的,希望能在1s后获取到promise的返回值 。 第二个then函数是在 5s之后执行的,第二次hello word 和promise的开始时间差约为5s。因为在该promise创建的1s后已经resolve,此时就直接调用then函数,不会延时1s执行。因为promise是只会执行一次。

那么再来看obsrvables

const Observable = require('rxjs/Observable').Observable;

let time;
const waitOneSecondObservable = new Observable((observer) => {
    console.log('I was called');
    time = new Date().getTime();
    setTimeout(() => observer.next('hey girl'), 1000);
});

waitOneSecondObservable.subscribe((value) => {console.log( '第一次', value, new Date().getTime() - time)});

setTimeout(() => {
    waitOneSecondObservable.subscribe((value) => {console.log( '第二次', value, new Date().getTime() - time)});
}, 5000)

// 输出
I was called
第一次 hey girl 1003
I was called
第二次 hey girl 1003
复制代码

这个就是我们希望的结果,他在每一次订阅的时候都会重新去执行被监听的函数,不论什么时候想要用这个函数,只需要重新 subscribe 一下就可以。

用observable已经可以实现多次订阅,但是这有时候可能不能符合我们的业务场景,在http请求中,我们可能希望只发一次请求,但是结果被多个订阅者共用。 Observables 本身没有提供这个功能,我们可以用 RxJS 这个库来实现,它有一个 share 的 operator。

const waitOneSecondObservable = new Observable((observer) => {
    // 发送http请求
});

const sharedWaitOneSecondObservable = 
    waitOneSecondObservable.share();

sharedWaitOneSecondObservable.subscribe(doSomething);

sharedWaitOneSecondObservable.subscribe(doSomethingElse);

// 使用了share,虽然subscribe了多次,但是仅发送一次请求,share了结果。
复制代码

一直是异步 & 可能是异步

const promise = new Promise((resolve) => {
    resolve(5);
});

promise.then(value => console.log(value + '!'));

console.log('And now we are here.');

// 
And now we are here.
5!
复制代码

虽然在promise里面 resolve了一个同步的东西,但他还是会先执行完代码。

const Observable = require('rxjs/Observable').Observable;

const observable = new Observable((observer) => {
    // observer.next(5);
    setTimeout(() => {
        observer.next(5);
    })
});

observable.subscribe(value => console.log(value + '!'));
console.log('And now we are here.');

// 
这个如果是直接next 5,则输出是  5! -> And now we are here.
采用setTimeout next 5, 则相反  And now we are here.-> 5!
复制代码

promise一直是异步, Observables则比较灵活,是否为异步得根据自己的函数来定,这点也比较危险。rxjs中有一些操作符可以让监听强制为异步的方式,例如 observeOn。

关注下面的标签,发现更多相似文章
评论