var observable = Rx.Observable.create(function(observer) {
observer.onNext('Simon');
observer.onNext('Jen');
observer.onNext('Sergi');
observer.onCompleted(); // 成功完成
});
当我们订阅这个Observable,它通过调用onNext()发射三个字符串给他的订阅者(监听者),最后调用onCompleted()标识这个序列完成了,但是我们是如何准确的订阅一个Observable了?通过Observers(也就是订阅者)。(补充:Observable可翻译为,可被订阅者,但是感觉怪怪的,就直译了)
var observer = Rx.Observer.create(
function onNext(x) { console.log('Next: ' + x); },
function onError(err) { console.log('Error: ' + err); },
function onCompleted() { console.log('Completed'); }
);
在Rx.Observer对象的create方法中有onNext,onCompleted,onError三个方法,并且他返回的也是一个Observer的对象实例。这三个方法是可选的,你根据你的需求来决定。例如,我们订阅一个无限的序列(比如按钮的点击事件,用户可以一直保持点击),onCompleted函数将永不会调用;再如我们确定某个序列将不会发生错误(例如数字数组的observable),我们也是不需要onError方法的。
如何创建observable获取远程内容,我们将会使用Rx.Observable.create来封装XMLHttpRequest对象:
function get(url) {
return Rx.Observable.create(function(observer) {
// Make a traditional Ajax request
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function() {
if (req.status == 200) {
// If the status is 200, meaning there have been no problems,
// Yield the result to listeners and complete the sequence
observer.onNext(req.response);
observer.onCompleted();
}
else {
// Otherwise, signal to listeners that there has been an error
observer.onError(new Error(req.statusText));
}
};
req.onerror = function() {
observer.onError(new Error("Unknown Error"));
};
req.send();
});
}
// Create an Ajax Observable
var test = get('/api/contents.json');
在上面的代码中,使用create封装XMLHttpRequest的get函数,如果HTTP GET请求成功了,我们将发送它的内容并完成那个序列(我们的observable讲仅仅发送一个结果),否则我们将发射一个错误。在最后一行,我是使用一个特定的url去调用这个函数。这将会产生一个observable,但是它不会发出任何请求,这个很重要:observable不会做任何事,直到最少有一个observer订阅它,所以让我们接着如下:
// Subscribe an Observer to it
test.subscribe(
function onNext(x) { console.log('Result: ' + x); },
function onError(err) { console.log('Error: ' + err); },
function onCompleted() { console.log('Completed'); }
);
首先我们必须注意的是:我们不是很明确地创建一个observer像上面的代码中那样,大部分的时候我们会使用一个局部的版本(在observable中我们调用subscribe操作符,使用observer的三种可选函数:onNext,onCompleted,onError)。subscribe设置所有的动态的设置所有的事情。在订阅(subscription)之前,我们仅仅定义声明observable和observer的交互,他们只会在我们调用subscribe后才开始交互。
Rx.DOM.get('/api/contents.json').subscribe(
function onNext(data) { console.log(data.response); },
function onError(err) { console.error(err); }
);
Rx.Observable
.from(['Adrià', 'Jen', 'Sergi'])
.subscribe(
function(x) { console.log('Next: ' + x); },
function(err) { console.log('Error:', err); }
function() { console.log('Completed'); }
);
var allMoves = Rx.Observable.fromEvent(document, 'mousemove');
allMoves.subscribe(function(e) {
console.log(e.clientX, e.clientY);
});
var movesOnTheRight = allMoves.filter(function(e) {
return e.clientX > window.innerWidth / 2;
});
var movesOnTheLeft = allMoves.filter(function(e) {
return e.clientX < window.innerWidth / 2;
});
movesOnTheRight.subscribe(function(e) {
console.log('Mouse is on the right:', e.clientX);
});
movesOnTheLeft.subscribe(function(e) {
console.log('Mouse is on the left:', e.clientX);
});
在上面的代码中,我们根据原始的allMoves创建了两个Observable,这些从初始Observable生成的特定Observable包含仅仅一个过滤器选项:movesOnTheRight包含屏幕右边发生的的event事件,movesOnTheLeft包含屏幕左边发生的event事件。他们都没有改变原始的Observable:allMoves依然保持发射所有的鼠标移动。Observable是不可改变的,每一涉及到它的操作符都是创建的一个新的Observable。
如果使用第三方的js库基于callback编写代码进行交互总有好多意外。使用fromCallback和fromNodeCallback两个函数我们可以把我们的Callback转换为Observable。Node.js总是遵循着调用回调函数首先使用一个error的参数告诉回调函数,发生了错误。当我们使用fromNodeCallback去创建指定的Node.js风格的回调函数:
var Rx = require('rx'); // Load RxJS
var fs = require('fs'); // Load Node.js Filesystem module
// Create an Observable from the readdir method
var readdir = Rx.Observable.fromNodeCallback(fs.readdir);
// Send a delayed message
var source = readdir('/Users/sergi');
var subscription = source.subscribe(
function(res) { console.log('List of directories: ' + res); },
function(err) { console.log('Error: ' + err); },
function() { console.log('Done!'); });