异步数据流的响应式编程库Rxjs(十一)- Rxjs VS Promise

232 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

Rxjs VS Promise

如果我们在项目中没有使用Rxjs,那必然会使用Promise(当然也可以使用基于Promise的第三方库),在某些方面,他们有相同之处,也有很大的区别。

单值和多值

Promise是单值的,一旦Promise的状态变为Resolved,Promise的值就定下来了不能再次被改变,但是一个Promise可以被多次订阅,他们会收到同样的值。

const  p1 = new Promise((resolve, reject) => {
    resolve(1);
    resolve(2);
});

p1.then((x) => {
   console.log(x);
});

// 只能接收到1,

Rxjs则不同,因为流是一个按时间顺序排列数据序列集合,是可以发出多个值的,直到这个流完成。

const source$ = new Observable((observer) => {
   observer.next(1);
   observer.next(2);
   observer.next(3);
   observer.complete();
});

source$.subscribe((x) => {
    console.log(x);
});

ObservablePromise都是可以被多次订阅的,这一点是类似的。

立即执行和惰性执行

当你new了一个Promsie,这个Promise会立即执行,即使还没有订阅,也就是还没有thenObservable则是惰性执行,你new了一个Observable,但是它并不会执行,只有当你subscribe订阅的时候才会执行。这一点非常重要,即使你很擅长Promise,在Angular中使用httpClient去发送网络请求的时候这是一个必踩的坑,就是你的Observable不会立即去发送网络请求,直到你订阅了它。

const  p1 = new Promise((resolve, reject) => {
    // 立即执行
    resolve(1);
    resolve(2);
});
const source$ = new Observable((observer) => {
   console.log('start!');
   observer.next(1);
   observer.next(2);
   observer.next(3);
   observer.complete();
});

// 订阅了才执行
source$.subscribe((x) => {
    console.log(x);
});

我觉得这个是Rxjs实现的很好的一点,如果你的网络请求用Promise来实现,当你定义了一个Promise的时候,你的网络请求已经发出去了,但是后面由于一些情况,并没有then,这就造成了浪费了,但是Observable则很好的优化了这一点。

可取消和不可取消

Promise的订阅是不可取消的,Observable的订阅是可以取消订阅的。

const source$ = new Observable((observer) => {
    let i=0;

    let id = setInterval(() => {
        observer.next(i++);
    }, 1000);

    return () => {
        clearInterval(id);
    };
});

let subscription = source$.subscribe((x) => {
    console.log(x);
});

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

console.log('End');

// End
// 0
// 1
// 2
// 3

一次执行和多次执行

Promise只会执行一次,而Observable可以有多个订阅者,而且会执行多次。

const source$ = new Observable((observer) => {
    let i=0;

    observer.next(i++);
    observer.next(i++);

    observer.complete();
});

let subscription1 = source$.subscribe((x) => {
    console.log('第一个: ', x);
});

let subscription2 = source$.subscribe((x) => {
    console.log('第二个: ', x);
});

console.log('End');

// 第一个:  0
// 第一个:  1
// 第二个:  0
// 第二个:  1
// End

同步和异步

对于Promise,订阅函数then永远是异步的。Observable的订阅函数,可能是异步的,也可能是同步的。

const  p1 = new Promise((resolve, reject) => {
    // 立即执行
    resolve(1);
    resolve(2);
});

p1.then((data) => {
  // 异步
  ...
});
  
console.log('End');

Observable可异步,可同步,要根据具体情况来看。如果使用interval创建的Observable,就是异步的,如果使用of创建的Observable,则是同步的,这一点非常重要。但是,即使使用of创建的Observable,我们也可以指定异步调度器,让它专为异步。

const source$ = interval(1000).pipe(take(5));

source$.subscribe((x) => {
    console.log(x);
});

console.log('End');
const source$ = of(1,2,3);

source$.subscribe((x) => {
    console.log(x);
});

console.log('End');
const source$ = of(1,2,3);

source$.pipe(subscribeOn(asyncScheduler)).subscribe((x) => {
    console.log(x);
});

console.log('End');

至此,是不是对Rxjs和Promise有了更进一步的理解了。 再来看一下在React中怎么使用Rxjs吧,这一部分暂时合并在本篇文章中,内容篇幅比较少,不适合单独写一篇文章。

Rxjs在React中的应用

Rxjs并不是Angular的专属库,JS的前端框架都是可以使用Rxjs的,只是Angular深度集成了Rxjs。这里只举几个简单的栗子。

React组件中使用Rxjs,最简单的用法就是:在组件初始挂载的时候,订阅一个数据流;在组件卸载的时候,取消订阅这个数据流

订阅事件流

import { fromEvent } from "rxjs";

const Component = () => {
  useEffect(() => {
    let subscription = fromEvent(document.body, "click").subscribe((e) => {
      console.log("click", e);
    });
    return () => subscription.unsubscribe();
  }, []);
  return <div></div>;
};

只要掌握了Rxjs,无论在哪个框架里使用,其实都是一样的。

总结

又回到了只是积累的这个问题上,如果之前就对Promise有深入的理解,那对比学习Rxjs的时候就相对轻松一些,两个都有深入理解之后,对比两者的时候就更加游刃有余了。