阅读 151

Observable与Promise的对比

Observable 是 Rxjs 库里异步编程核心的一部分,相比于 Rxjs 又有那些区别呢

Observable 介绍

RxJS 是一个库,它通过使用 observable 序列来编写异步和基于事件的程序。它提供了一个核心类型 Observable,附属类型 (Observer、 Schedulers、 Subjects) 和受 [Array#extras] 启发的操作符 (map、filter、reduce、every, 等等),这些数组操作符可以把异步事件作为集合来处理。 ————引用 Rxjs 中文网站的介绍 ——Rxjs 官网

Observable(可观察对象)

这里具体原理可参照 JavaScript 设计模式--观察,订阅模式

  • 在 RxJS 中用来解决异步事件管理的的基本概念是:
  • -Observable (可观察对象): 表示一个概念,这个概念是一个可调用的未来值或事件的集合。
  • Observer (观察者): 一个回调函数的集合,它知道如何去监听由 Observable 提供的值。
  • Subscription (订阅): 表示 Observable 的执行,主要用于取消 Observable 的执行。
  • Operators (操作符): 采用函数式编程风格的纯函数 (pure function),使用像 map、filter、concat、flatMap 等这样的操作符来处理集合。
  • Subject (主体): 相当于 EventEmitter,并且是将值或事件多路推送给多个 Observer 的唯一方式。
  • Schedulers (调度器): 用来控制并发并且是中央集权的调度员,允许我们在发生计算时进行协调,例如 setTimeout 或 requestAnimationFrame 或其

使用 Observable

//Observable写法
import { Observable } from 'rxjs'
//创建一个Observable对象
let Ob1 = new Observable((observer) => {
  observer.next('observable')
})
Ob1.subscribe((value) => {
  console.log(value)
})
复制代码

使用 Promise

let Pro1 = new Promise((resolve,reject)=>{
  resolve(1)
  //reject(2)
}
Pro1.then(value=>{
  console.log(value)
})
复制代码

Observable 与函数

function foo() {
  console.log('Hello');
  return 42;
}

var x = foo.call(); // 等同于 foo()
console.log(x);
var y = foo.call(); // 等同于 foo()
console.log(y);
//"Hello"
//42
//"Hello"
//42
//
用 Observables 重写上面的代码:
var foo = Rx.Observable.create(function (observer) {
  console.log('Hello');
  observer.next(42);
});

foo.subscribe(function (x) {
  console.log(x);
});
foo.subscribe(function (y) {
  console.log(y);
});
//"Hello"
//42
//"Hello"
//42
//
复制代码

因为函数和 Observables 都是惰性运算。如果你不调用函数,console.log('Hello') 就不会执行。Observables 也是如此,如果你不“调用”它(使用 subscribe),console.log('Hello') 也不会执行。此外,“调用”或“订阅”是独立的操作:两个函数调用会触发两个单独的副作用,两个 Observable 订阅同样也是触发两个单独的副作用。EventEmitters 共享副作用并且无论是否存在订阅者都会尽早执行,Observables 与之相反,不会共享副作用并且是延迟执行。

区别

为什么用 Rxjs

promise 相较于 Rxjs 而言功能更单一 promise 只能将一个数据的状态由 pending 转换成 resoloved 或者 rejected.而 Rxjs 可以处理多个数据对应 complete 和 error 状态但是 Rxjs 同时又拥有 next 方法。Observable 是惰性的,需要 subscribe 的时候才输出值。promise 内部状态是不可控制的,执行了就无法终止。而 Observable 可以定义如何取消异步方法。 比如如下的场景:

输入框中输入字符,按回车发送一个请求,并将返回的结果变成一个 Todo item。如果在请求返回结果前又一次按下回车或 add 按钮,如果相同则不进行任何操作,如果不同则取消掉上次的请求并发送新的请求。(实际的场景往往是发送个 http 请求该请求会返回的很慢,业务上加上心跳检查(发送方按照一定规则(周期性发送、空闲发送等等)向接收方发送固定格式的消息,接受方收到消息后回复一个固定格式的消息,如果长时间没有收到,比如心跳周期的 3 倍,则认为当前连接失效,将其断开。),如果前一次在心跳间隔内无返回则再次调用请求,同时需要抛弃前一次请求的返回,并且此时有可能调用参数不一样的该接口从而造成数据不一致的问题)对于 Promise 实现我们不只要维护一个定时器 timer 同时还要维护一个全局变量。

再比如我们需要监听页面滚动的事件,作出一些逻辑操作,这是就会产生事件过于频繁的调用,造成页面卡顿的现象。用原生 js 实现的时候,需要实现个节流或者防抖函数,通过实现个闭包函数,在内部维护个定时器。而在 Rxjs 中通过操作符 debounce 就可以方便的解决

同步与异步

首先第一个区别是:Promise 是解决异步编程的方法,而 Observable 可以同步也可以异步

//Observable同步执行
var foo = Rx.Observable.create(function (observer) {
  console.log('Hello')
  observer.next(42)
  observer.next(100) // “返回”另外一个值
  observer.next(200) // 还可以再“返回”值
})

console.log('before')
foo.subscribe(function (x) {
  console.log(x)
})
/*
"before"
"Hello"
42
100
200
"after"
*/
console.log('after')

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
复制代码
单个值和多个值

Observable 里面的 next()方法类似与 Promise.resolve(),但是 Promise()内部的 resolve()方法成功后,后面的不会执行,就是说 Observable 可以持续发射很多值,而 Promise 只能发射一个值就结束了

const promise1 = new Promise((resolve) => {
  console.log(' I am promise ')
})
const observable1 = new Observable(() => {
  console.log('I am Observable!')
})
复制代码

Promise 里的 console.log 会执行,而 Observable 里的代码不会输出,只有订阅(subscribe)之后才会输出

异步执行任务的取消

Promise 的执行默认是不可取消的,而 Observable 是可以通过 subscribe 的 unsubscribe()方法来取消 因为在 Promise 中.then()返回的是一个新的 Promise 对象,而 Observable+subscribe 返回的是 subscription 对象

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!
//Observable

复制代码
一次和多次执行

Promise 只会执行一次 resolve(),而 Observable 每次订阅都会执行一次观察对象中的代码


//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

//Observable

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
复制代码
工具

Observable 提供了很多的工具函数,常用的 filter 和 map 演示如下

let stream2$ =
  new Observable() <
  number >
  ((observer) => {
    let count = 0
    let interval = setInterval(() => {
      observer.next(count++)
    }, 1000)
    return () => {
      clearInterval(interval)
    }
  })
stream2$.subscribe((value) => console.log('Observable>' + value))
stream2$
  .pipe(filter((val) => val % 2 == 0))
  .subscribe((value) => console.log('filter>' + value))
stream2$
  .pipe(map((value) => value * value))
  .subscribe((value) => console.log('map>' + value))
复制代码
总结
  • Observable 是 lazy 的,只有订阅才输出,同时 Observable 可以接受多个值。Promise 只能 resolve 一次。
  • Promise 内部 resolve 一定是异步的,而 Observable 则可以自由发挥
  • Promise 只会执行一次,创建之后就会执行,不能重复执行,而 Observable 每订阅一次会执行一次,而如果想只执行一次,可以利用 share。
  • Promise 不可以取消,而 Observable 可以取消
文章分类
前端