使用promise收集未知大小的分页数据

Collecting paginated data of unknown size using promises

本文关键字:分页 数据 未知 promise 使用      更新时间:2023-09-26

我正在查询一个REST-API以获取所有组。这些团体一批50人。在继续处理它们之前,我想收集所有这些。

到目前为止,我依赖于回调,但我希望使用promise来链接所有组的检索,然后进一步处理结果数组。

我只是不太明白如何使用promise替换递归函数调用。

我该如何使用A+promise来逃避我用这段代码创建的回调地狱

function addToGroups() {
   var results = []
   collectGroups(0)
   function collectGroups(offset){
     //async API call
     sc.get('/tracks/'+ CURRENT_TRACK_ID +'/groups?limit=50&offset=' + offset , OAUTH_TOKEN, function(error, data){
       if (data.length > 0){
         results.push(data)
         // keep requesting new groups
         collectGroups(offset + 50)
       }
       // finished
       else {
         //finish promise
       }
     })
   }
 }

使用标准承诺,包装所有现有代码,如下所示:

function addToGroups() {
    return new Promise(function(resolve, reject) {
        ...  // your code, mostly as above
    });
}

在代码中,完成后调用resolve(data),如果由于某种原因调用链失败,则调用reject()

为了使整个事情更像"promise",首先让函数collectGroups返回一个promise:

function promiseGet(url) {
    return new Promise(function(resolve, reject) {
        sc.get(url, function(error, data) {
            if (error) {
                reject(error);
            } else {
                resolve(data);
            }
        });
    }
}
// NB: promisify-node can do the above for you
function collectGroups(offset, stride) {
    return promiseGet('/tracks/'+ CURRENT_TRACK_ID +'/groups?limit=' + stride + '&offset=' + offset , OAUTH_TOKEN);
}

然后在你的代码中使用这个Promise:

function addToGroups() {
    var results = [], stride = 50;
    return new Promise(function(resolve, reject) {
        (function loop(offset) {
            collectGroups(offset, stride).then(function(data) {
                if (data.length) {
                    results.push(data);
                    loop(offset + stride);
                } else {
                    resolve(data);
                }
            }).catch(reject);
        )(0);
    });
}

这是可行的。我正在使用https://github.com/kriskowal/q承诺。

  var Q = require('q');
  function addToGroups() {
     var results = []
     //offsets hardcoded for example
     var a = [0, 51, 101];
     var promises = [], results;
     a.forEach(function(offset){
        promises.push(collectGroups(offset));
     })
     Q.allSettled(promises).then(function(){
        promises.forEach(function(promise, index){
          if(promise.state === 'fulfilled') {
            /* you can use results.concatenate if you know promise.value (data returned by the api)
               is an array */
            //you also could check offset.length > 0 (as per your code)
            results.concatenate(promise.value); 
            /*
              ... do your thing with results ...
            */
          }
          else {
            console.log('offset',index, 'failed', promise.reason);
          }
        });
     });
   }
  function collectGroups(offset){
    var def = Q.defer();
    //async API call
    sc.get('/tracks/'+ CURRENT_TRACK_ID +'/groups?limit=50&offset=' + offset , OAUTH_TOKEN, function(error, data){
      if(!err) {
        def.resolve(data);
      }
      else {
        def.reject(err);
      }
    });
    return def.promise;
  }

如果有效,请告诉我。

以下是使用spex.sequence:的完整示例

var spex = require("spex")(Promise);
function source(index) {
    return new Promise(function (resolve) {
        sc.get('/tracks/' + CURRENT_TRACK_ID + '/groups?limit=50&offset=' + index * 50, OAUTH_TOKEN, function (error, data) {
            resolve(data.length ? data : undefined);
        });
    });
}
spex.sequence(source, {track: true})
    .then(function (data) {
        // data = all the pages returned by the sequence;
    });

我认为没有比这更简单的了;)