最近在学习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。