最近在學(xué)習(xí)RxJS嘉赎,它是使用 Observables 的響應(yīng)式編程的庫,它使編寫異步或基于回調(diào)的代碼更容易于樟。
下面主要介紹Observables 與 promise的不同點(diǎn)公条。
單值與多值
const numberPromise = new Promise((resolve) => {
resolve(5);
resolve(10)
});
numberPromise.then(value => console.log(value));
// 輸出 只會(huì)有 5
下面改寫為observables的寫法,使用 next 替代 promise 的 resolve迂曲, 用subscribe 取代then來訂閱結(jié)果靶橱。
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是可以連續(xù)訂閱的,這個(gè)和promise的區(qū)別很大路捧。平時(shí)我們遇到的可能大多數(shù)都是一個(gè)請求一個(gè)響應(yīng)的這種情況关霸,但是我們也會(huì)存在一些情況:
- setInterval,需要resolve多個(gè)值
- 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
代碼執(zhí)行順序
const promise = new Promise((resolve) => {
console.log('promise call')
resolve(1);
console.log('promise end')
})
// 執(zhí)行這段代碼 promise call 和 promise end 會(huì)立即執(zhí)行
const observable = new Observable(() => {
console.log('I was called!');
});
// 此時(shí)并沒有console
// 只有 observable.subscribe(); 這個(gè)時(shí)候 I was called杰扫!才會(huì)被打印出來队寇。
上面兩段代碼就對比可以發(fā)現(xiàn)Observables是lazy的,只有當(dāng)有人去訂閱(subscribe)的時(shí)候Observables才會(huì)真正的被執(zhí)行章姓。
如果上方setInterval的函數(shù)寫在promise里面佳遣,但是沒有promise.then之類的函數(shù)就會(huì)造成資源的浪費(fèi),而在observable里面凡伊,不訂閱連內(nèi)存都不會(huì)分配零渐。
不能取消 & 能取消
promise默認(rèn)是不能取消的,可以使用promise的實(shí)現(xiàn)庫 bluebird 來實(shí)現(xiàn)系忙。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)
// 結(jié)果
0!
1!
2!
3!
這個(gè)地方需要注意的是, subscribe 返回的不是一個(gè)Observable! 這就是說不能和promise一樣鏈?zhǔn)降膕ubscribe银还。subscribe返回的是一個(gè)對于指定observable的 Subscription风宁。他只有一個(gè)方法可以調(diào)用,就是unsubscribe蛹疯。
單個(gè)訂閱&多個(gè)訂閱
promise 是比較激進(jìn)的杀糯,在一個(gè)promise被創(chuàng)建的時(shí)候,他就已經(jīng)執(zhí)行了苍苞,并且不能重復(fù)的被執(zhí)行了固翰。
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)
// 輸出結(jié)果是 promise call
第一次 hello world 1007
第二次 hello world 5006
上面這個(gè)例子中,我創(chuàng)建了一個(gè)promise羹呵,他是立即執(zhí)行的setTimeout,所以在第一個(gè)then函數(shù)中打印時(shí)間間隔是約等于 1s骂际,這個(gè)是符合我們預(yù)期的,希望能在1s后獲取到promise的返回值 冈欢。 第二個(gè)then函數(shù)是在 5s之后執(zhí)行的歉铝,第二次hello word 和promise的開始時(shí)間差約為5s。因?yàn)樵谠損romise創(chuàng)建的1s后已經(jīng)resolve凑耻,此時(shí)就直接調(diào)用then函數(shù)太示,不會(huì)延時(shí)1s執(zhí)行柠贤。因?yàn)閜romise是只會(huì)執(zhí)行一次。
那么再來看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
這個(gè)就是我們希望的結(jié)果类缤,他在每一次訂閱的時(shí)候都會(huì)重新去執(zhí)行被監(jiān)聽的函數(shù)臼勉,不論什么時(shí)候想要用這個(gè)函數(shù),只需要重新 subscribe 一下就可以餐弱。
用observable已經(jīng)可以實(shí)現(xiàn)多次訂閱宴霸,但是這有時(shí)候可能不能符合我們的業(yè)務(wù)場景,在http請求中膏蚓,我們可能希望只發(fā)一次請求瓢谢,但是結(jié)果被多個(gè)訂閱者共用。 Observables 本身沒有提供這個(gè)功能驮瞧,我們可以用 RxJS 這個(gè)庫來實(shí)現(xiàn)氓扛,它有一個(gè) share 的 operator。
const waitOneSecondObservable = new Observable((observer) => {
// 發(fā)送http請求
});
const sharedWaitOneSecondObservable =
waitOneSecondObservable.share();
sharedWaitOneSecondObservable.subscribe(doSomething);
sharedWaitOneSecondObservable.subscribe(doSomethingElse);
// 使用了share论笔,雖然subscribe了多次幢尚,但是僅發(fā)送一次請求,share了結(jié)果翅楼。
一直是異步 & 可能是異步
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了一個(gè)同步的東西,但他還是會(huì)先執(zhí)行完代碼真慢。
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.');
//
這個(gè)如果是直接next 5,則輸出是 5毅臊! -> And now we are here.
采用setTimeout next 5, 則相反 And now we are here.-> 5黑界!
promise一直是異步管嬉, Observables則比較靈活,是否為異步得根據(jù)自己的函數(shù)來定朗鸠,這點(diǎn)也比較危險(xiǎn)蚯撩。rxjs中有一些操作符可以讓監(jiān)聽強(qiáng)制為異步的方式,例如 observeOn烛占。