我对反应式编程很陌生,我想在一个简短的代码片段上看看你对Rx思维方式的看法。
我的目的是从Web服务中获取大约2245个项目,每次最多返回500个项目。所以我必须进行几次调用并连接所有结果。
我将$ .getJSON返回的所有promises存储在一个数组中,然后使用forkJoin方法一起处理结果。
// get the exact total number of items
function getCount() {
return $.get({
url: '/sites/_api/items/count',
contentType: "application/json;odata=verbose",
headers: { "Accept": "application/json;odata=verbose" }
});
}
getCount().done(function (result) {
var promises = []; // store promises
var total = result.d.ItemCount; // total number of items
var batch = 500; // number of items to fetch for each request
var count = Math.ceil(total / batch); // number of request needed
for (var i = 0; i < count; i++) {
var skip = batch * i; // number of item already fetched
var top = batch * (i + 1) > total ? (total % batch) : batch; // number of item to fetch
var p = $.getJSON("/sites/_api/items?$select=" + fieldname + "&$skip=" + skip + "&$top=" + top); // get promise
promises.push(p); // store promise in dedicated array
}
// join all promises and handle all results at once
Rx.Observable.forkJoin(promises).subscribe(function (result) {
console.log(result); //[object, object, object, object, object]
});
});
这给了我预期的结果,但我想了解你是否有更好的方法来使用Rx。
------编辑--------- 我添加了基于流概念的第一次尝试的代码。意思是我有一个未知数量的请求发出。我将把结果加起来(参见onDataReceived函数)并在完成时将它们作为一个整体处理。除非我无法确定如何通知所有请求已完成并触发“已完成”#39;可观察的方法。
var items = [];
var obs;
function onDataReceived(data) {
obs.onNext(data);
}
function getFieldValue(fieldname, skip, top) {
var uri = "/sites/_api/items?&$skip=" + skip + "&$top=" + top;
$.getJSON(uri, onDataReceived);
}
function getCount() {
return $.get({
url: '/sites/_api/items/count',
contentType: "application/json;odata=verbose",
headers: { "Accept": "application/json;odata=verbose" }
});
}
obs = new Rx.Subject();
obs.subscribe(
function (value) { items = items.concat(value.d); },
function (err) { console.err(err); },
function () { console.log("completed" + items.length); }
);
getCount().done(function (result) {
var total = result.d.ItemCount;
var batch = 500;
var count = Math.ceil(total / batch);
for (var i = 0; i < count; i++) {
var skip = batch * i;
var top = batch * (i + 1) > total ? (total % batch) - 1 : batch;
getFieldValue(skip, top);
}
});
&#13;
答案 0 :(得分:0)
您的代码没有从Rx提供的所有内容中获益,而只是将其接口更改为Observable。它仍然是内部承诺。
Promise非常渴望,因此调用$_json()
将立即开始获取数据。如果可能的话,尽可能多地推出代码,或者至少用Rx.Observable.defer()
尝试让你的函数返回一个只在.subscribe
开始运行时的惰性Observable,参见下面的示例实现,其中包括限制(对于页面详细信息,有10个并发API请求)
function mock_get_count() {
return Rx.Observable.of(255)
.delay(500)
.toPromise(); // to mock $.get() signature (returning promise)
}
function mock_get_page(page) {
return Rx.Observable.of('')
.do(() => console.log('starting page ' + page))
.ignoreElements()
.concat(
Rx.Observable.range(10 * page, 10)
.toArray()
.delay(1000 + Math.random() * 2000) /* slow api, response takes 1-3 seconds*/
)
.toPromise() /* to mock your $.JSON() signature (returning promise) */
;
}
function getAllResults() {
return Rx.Observable.defer(() => mock_get_count())
.mergeMap(itemCount => {
const pages = Math.ceil(itemCount / 10);
console.log('itemCount: ' + itemCount + ', pages: ' + pages);
return Rx.Observable.range(0, pages)
.mergeMap(
p => Rx.Observable.defer(() => mock_get_page(p)),
null,
10 /* concurrent requests to API */
);
})
}
getAllResults().subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.1.0/Rx.js"></script>