在本章中,使用Rxjs,我们将创建一个典型的web application。我们将转化Dom Object Model(DOM)并且使用node.js中的websocket做c/s的交互。
var quakes = Rx.Observable
.interval(5000)
.flatMap(function() {
return Rx.DOM.jsonpRequest({
url: QUAKE_URL,
jsonpCallback: 'eqfeed_callback'
}).retry(3);
})
.flatMap(function(result) {
return Rx.Observable.from(result.response.features);
})
.distinct(function(quake) { return quake.properties.code; });
quakes.subscribe(function(quake) {
var coords = quake.geometry.coordinates;
var size = quake.properties.mag * 10000;
L.circle([coords[1], coords[0]], size).addTo(map);
});
function initialize() {
var quakes = Rx.Observable
.interval(5000)
.flatMap(function() {
return Rx.DOM.jsonpRequest({
url: QUAKE_URL,
jsonpCallback: 'eqfeed_callback'
});
})
.flatMap(function(result) {
return Rx.Observable.from(result.response.features);
})
.distinct(function(quake) { return quake.properties.code; });
quakes.subscribe(function(quake) {
var coords = quake.geometry.coordinates;
var size = quake.properties.mag * 10000;
L.circle([coords[1], coords[0]], size).addTo(map);
});
}
Rx.DOM.ready().subscribe(initialize);
<table>
<thead>
<tr>
<th>Location</th>
<th>Magnitude</th>
<th>Time</th>
</tr>
</thead>
<tbody id="quakes_info">
</tbody>
</table>
function makeRow(props) {
var row = document.createElement('tr');
row.id = props.net + props.code;
var date = new Date(props.time);
var time = date.toString();
[props.place, props.mag, time].forEach(function(text) {
var cell = document.createElement('td');
cell.textContent = text;
row.appendChild(cell);
});
return row;
}
var table = document.getElementById('quakes_info');
quakes
.pluck('properties')
.map(makeRow)
.subscribe(function(row) { table.appendChild(row); });
var onMove = Rx.Observable.fromEvent(document, 'mousemove');
var subscriber1 = onMove.subscribe(function(e) {
console.log('Subscriber1:', e.clientX, e.clientY);
});
var subscriber2 = onMove.subscribe(function(e) {
console.log('Subscriber2:', e.clientX, e.clientY);
});
// Result:
// Subscriber1: 23 24
// Subscriber2: 23 24
// Subscriber1: 34 37
// Subscriber2: 34 37
// Subscriber1: 46 49
// Subscriber2: 46 49
// …
function printValue(value) {
console.log(value);
}
var rangeToFive = Rx.Observable.range(1, 5);
var obs1 = rangeToFive.subscribe(printValue); // 1, 2, 3, 4, 5
var obs2 = Rx.Observable
.delay(2000)
.flatMap(function() {
return rangeToFive.subscribe(printValue); // 1, 2, 3, 4, 5
});
var source = Rx.Observable.interval(2000);
var observer1 = source.subscribe(function (x) {
console.log('Observer 1, next value: ' + x);
});
var observer2 = source.subscribe(function (x) {
console.log('Observer 2: next value: ' + x);
});
》
Observer 1, next value: 0
Observer 2: next value: 0
Observer 1, next value: 1
Observer 2: next value: 1
…
var source = Rx.Observable.interval(1000);
var observer1 = source.subscribe(function (x) {
console.log('Observer 1: ' + x);
});
setTimeout(function() {
var observer2 = source.subscribe(function (x) {
console.log('Observer 2: ' + x);
});
}, 3000);
》
Observer 1: 0
Observer 1: 1
Observer 1: 2
Observer 1: 3
Observer 2: 0
Observer 1: 4
Observer 2: 1
…
// Create an Observable that yields a value every second
var source = Rx.Observable.interval(1000);
var publisher = source.publish();
// Even if we are subscribing, no values are pushed yet.
var observer1 = publisher.subscribe(function (x) {
console.log('Observer 1: ' + x);
});
// publisher connects and starts publishing values
publisher.connect();
setTimeout(function() {
// 5 seconds later, observer2 subscribes to it and starts receiving
// current values, not the whole sequence.
var observer2 = publisher.subscribe(function (x) {
console.log('Observer 2: ' + x);
});
}, 5000);
》
index.html:29 Observer 1: 0
index.html:29 Observer 1: 1
index.html:29 Observer 1: 2
index.html:29 Observer 1: 3
index.html:29 Observer 1: 4
index.html:29 Observer 1: 5
index.html:37 Observer 2: 5
…
var quakes = Rx.Observable
.interval(5000)
.flatMap(function() {
return Rx.DOM.jsonpRequest({
url: QUAKE_URL,
jsonpCallback: 'eqfeed_callback'
});
})
.flatMap(function(result) {
return Rx.Observable.from(result.response.features);
})
.distinct(function(quake) { return quake.properties.code; })
➤ .share()
现在,quakes表现的像个hot Observable,我们米有必要担心有多少observer我们连接了,因为他们将会接收到严格的同样数据。
var table = document.getElementById('quakes_info');
quakes
.pluck('properties')
.map(makeRow)
❶ .bufferWithTime(500)
❷ .filter(function(rows) { return rows.length > 0; }
.map(function(rows) {
var fragment = document.createDocumentFragment();
rows.forEach(function(row) {
❸ fragment.appendChild(row);
});
return fragment;
})
.subscribe(function(fragment) {
❹ table.appendChild(fragment);
});
var codeLayers = {};
var quakeLayer = L.layerGroup([]).addTo(map);
quakes.subscribe(function(quake) {
var coords = quake.geometry.coordinates;
var size = quake.properties.mag * 10000;
var circle = L.circle([coords[1], coords[0]], size).addTo(map);
➤ quakeLayer.addLayer(circle);
➤ codeLayers[quake.id] = quakeLayer.getLayerId(circle);
});
❶ var identity = Rx.helpers.identity;
function isHovering(element) {
❷ var over = Rx.DOM.mouseover(element).map(identity(true));
❸ var out = Rx.DOM.mouseout(element).map(identity(false));
❹ return over.merge(out);
}
var table = document.getElementById('quakes_info');
quakes
.pluck('properties')
.map(makeRow)
.bufferWithTime(500)
.filter(function(rows) { return rows.length > 0; })
.map(function(rows) {
var fragment = document.createDocumentFragment();
rows.forEach(function(row) {
fragment.appendChild(row);
});
return fragment;
})
.subscribe(function(fragment) {
var row = fragment.firstChild; // Get row from inside the fragment
❶ var circle = quakeLayer.getLayer(codeLayers[row.id]);
❷ isHovering(row).subscribe(function(hovering) {
circle.setStyle({ color: hovering ? '#ff0000' : '#0000ff' });
});
❸ Rx.DOM.click(row).subscribe(function() {
map.panTo(circle.getLatLng());
});
table.appendChild(fragment);
})
function getRowFromEvent(event) {
return Rx.Observable
.fromEvent(table, event)
❶ .filter(function(event) {
var el = event.target;
return el.tagName === 'TD' && el.parentNode.id.length;
})
❷ .pluck('target', 'parentNode')
❸ .distinctUntilChanged();
}
getRowFromEvent('mouseover')
.pairwise()
.subscribe(function(rows) {
var prevCircle = quakeLayer.getLayer(codeLayers[rows[0].id]);
var currCircle = quakeLayer.getLayer(codeLayers[rows[1].id]);
prevCircle.setStyle({ color: '#0000ff' });
currCircle.setStyle({ color: '#ff0000' });
});
getRowFromEvent('click')
.subscribe(function(row) {
var circle = quakeLayer.getLayer(codeLayers[row.id]);
map.panTo(circle.getLatLng());
});
quakes
.pluck('properties')
.map(makeRow)
.subscribe(function(row) { table.appendChild(row); });