一、拉取(Pull) 、 推送(Push)
Observer Pattern - (观察者模式)
Observable(可观察对象)是基于推送(Push)运行时执行(lazy)的多值集合。下方表格对Observable进行了定位(为解决基于推送的多值问题):下表是对Observable
的使用场景进行的简单的定位。
MagicQ | 单值 | 多值 |
---|---|---|
拉取(Pull) | 函数 |
遍历器 |
推送(Push) | Promise |
Observable |
拉取(Pull) V.S. 推送(Push)
拉取和推送是数据生产者和数据消费者之间通信的两种不同机制。
何为拉取? 在拉取系统中,总是由消费者决定何时从生产者那里获得数据。生产者对数据传递给消费者的时间毫无感知(被动的生产者,主动的消费者)。
JavaScript函数是典型的拉取系统:函数是数据的生产者,对函数进行调用的代码(消费者)从函数调用后的返回值中拉取单值进行消费。
// 函数是数据的生产者
let getLuckyNumber = function() {
return 7;
};
/* let代码段是数据的消费者,
* getLuckyNumber对调用时间毫无感知。
*/
let luckNumber = getLuckyNumber();
何为推送? 在推送系统中生产者决定何时向消费者传递数据,消费者对何时收到数据毫无感知(被动的消费者)。
现代JavaScript中Promise是典型的推送系统。作为数据生产者的Promise通过resolve()
向数据消费者——回调函数传递数据:与函数不同,Promise决定向回调函数推送值的时间。
MagicQ | 生产者 | 消费者 |
---|---|---|
拉取 | 被动: 在被请求时产生数据 | 主动: 决定何时请求数据 |
推送 | 主动: 控制数据的产生逻辑 | 被动: 获得数据后进行响应 |
observable
被订阅后,会立即(
同步地)推送
1
,
2
,
3
三个值;1秒之后,继续推送
4
这个值,最后结束(
推送结束通知):
var observable = Rx.Observable.create(function (observer) {
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
});
为得到observable
推送的值,我们需要订阅(subscribe)这个Observable:(通过.subscribe这个方法进行订阅行为)
var observable = Rx.Observable.create(function (observer) { // 此处是创建一个被观察者observable
observer.next(1);
observer.next(2);
observer.next(3);
setTimeout(() => {
observer.next(4);
observer.complete();
}, 1000);
});
console.log('just before subscribe');
observable.subscribe({ // 此处是对observable 进行订阅,即创建一个观察者observe
next: x => console.log('got value ' + x),
error: err => console.error('something wrong occurred: ' + err),
complete: () => console.log('done'),
});
console.log('just after subscribe');
程序执行后,将在控制台输出如下结果:
just before subscribe
got value 1
got value 2
got value 3
just after subscribe
got value 4
done
Observable 更像是一个不需要传入参数的函数,它拓展了函数的概念使得它可以返回多个值。
看看下面的例子:
function foo() {
console.log('Hello');
return 42;
}
console.log('before');
console.log(foo.call()); // foo.call()等同于直接掉用foo()
console.log('after');
"before"
"Hello"
42
"after"
Observable的行为完全一样:
var foo = Rx.Observable.create(function (observer) {
console.log('Hello');
observer.next(42);
});
console.log('before');
foo.subscribe(function (x) {
console.log(x);
});
console.log('after');
"before"
"Hello"
42
"after"
订阅 foo
完全是同步的,与函数的调用一样。
Observable可以异步或者同步地产生数据。
那Observable 与函数的不同之处在哪里? Observable可以在一个时间过程中‘返回’多个值,而函数却不能。在函数中你不可以这么做:
function foo() {
console.log('Hello');
return 42;
return 100; // 这个语句永远不会被执行。
}
虽然函数只能有一个返回值,但是在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);
});
console.log('after');
输出结果如下:
"before"
"Hello"
42
100
200
"after"
你甚至可以异步地返回值:
var foo = Rx.Observable.create(function (observer) {
console.log('Hello');
observer.next(42);
observer.next(100);
observer.next(200);
setTimeout(() => {
observer.next(300); // happens asynchronously
}, 1000);
});
console.log('before');
foo.subscribe(function (x) {
console.log(x);
});
console.log('after');
输出结果:
"before"
"Hello"
42
100
200
"after"
300
结论:
func.call()
意味着“同步地给我一个值”
observable.subscribe()
意味着“不管是同步或者异步,给我一些值”
Rx.Observable.create
是 Observable
构造函数的别名,接受一个参数: subscribe
函数。
create
创建Observable,我们通常还使用 创建操作符
, 如
of
,
from
,
interval
, 等来创建Observable。
Observable在执行过程中,可以推送三种类型的值:
"Next" 通知: 实际产生的数据,包括数字、字符串、对象等
"Error" 通知:一个JavaScript错误或者异常
在Observable的执行过程中,0个或者多个“Next”通知会被推送。在错误或者完成通知被推送后,Observable不会再推送任何其他通知。
下面代码展示了Observable 在执行过程中推送3个“Next” 通知然后结束:
var observable = Rx.Observable.create(function subscribe(observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
});
Observable 严格遵守 Observable 契约,后面值为4
的“Next” 通知永远不会被推送:
var observable = Rx.Observable.create(function subscribe(observer) {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
observer.next(4); // 由于违法契约,4不会被推送
});
使用try
/catch
块包裹 subscribe
代码是一个很赞的想法,如果捕获了异常,可以推送错误通知:
var observable = Rx.Observable.create(function subscribe(observer) {
try {
observer.next(1);
observer.next(2);
observer.next(3);
observer.complete();
} catch (err) {
observer.error(err); // 捕获异常后推送错误通知
}
});