代码审查:这是遵循rxjs原则吗?

时间:2017-02-16 13:37:55

标签: rxjs

我对反应式编程很陌生,我想在一个简短的代码片段上看看你对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;可观察的方法。

&#13;
&#13;
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;
&#13;
&#13;

1 个答案:

答案 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>