具有延迟的递归函数

时间:2014-11-25 16:49:40

标签: javascript angularjs deferred angular-promise

我想使用递归函数,但每个函数都应在prevuse完成后运行。 所以我写这段代码:

var service = ['users', 'news'],
    lastSync = {
                 'users' : false,
                 'news' : false
               };
db.transaction(function (tx) {
   lastSyncFunc(tx,service,0).then(function(){
       console.log(lastSync);
   });
});

function lastSyncFunc(tx,service,index){
   deferred = $q.defer();
   tx.executeSql("SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", [service[index]], function (tx, result) {
       if (result.rows.length > 0) {
           lastSync[service[index]] = result.rows.item(0).fTime;
       }
       return ++index<service.length ? lastSyncFunc(tx,service,index) : deferred.resolve();
   });
   return deferred.promise;
}

现在我的程序返回false lastSync.userslastSync.users,因为在函数完全运行之前运行此部分。

2 个答案:

答案 0 :(得分:3)

手动多重异步调用处理并不总是最佳决策 您可以尝试使用$q.all()

简单来说,第二步应该为单个查询编写promisified版本:

const pDbExec = (tx, sql, params = []) => {
  let deferred = $q.defer();
  tx.executeSql(sql, params, (tx, res) => deferred.resolve(res));
  return deferred.promise();
}

第一步应该是“检查我使用的图书馆/方法的有效版本是否存在”。

然后,只需拨打$q.all,将您的服务列表映射到承诺:

const SQL_SvcLastSync = `SELECT time FROM ... LIMIT 1`;
db.transaction(tx => {
  $q.all(service.map(svc => pDbExec(tx, SQL_SvcLastSync, [svc])))
    .then(results => 
       results.map(res => 
         res.rows.length > 0 ? res.rows.item(0).fTime : null))
    .then(results => console.log(results));
});

要将结果格式化为键/值对,您有两个选择:

  • 添加减速机: .then(results => results.reduce((acc, res, i) => (acc[service[i]]=res, acc), {}))
  • 提供键/值(值为promises)而不是数组映射到$ q.all,因此它们将在相同的键下解析。
    你需要修改中间映射器,当然。

添加参数的简单解决方案,用于在递归调用之间保存相同的延迟对象:

尝试这样的事情:

function lastSyncFunc(tx,service,index, def){
   var deferred = def || $q.defer();
   tx.executeSql(
     "SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", 
     [service[index]], 
     function (tx, result) {
       if (result.rows.length > 0) {
           lastSync[service[index]] = result.rows.item(0).fTime;
       }
       return ++index<service.length ? 
         lastSyncFunc(tx,service,index, deferred) : 
         deferred.resolve();
   });
   return deferred.promise;
}

我只是提供延迟到maxdepth,我们可以在那里解决它。

答案 1 :(得分:1)

事实上这样做的方法是避免在“The Collection Kerfuffle”标题下的here所述的递归。

主张模式可以使用db.transaction(function() {...})结构中的所有内容进行编码,但更清楚的是将tx.executeSql(...)拉出并在单独的函数中进行宣传。

你最终会得到这样的东西:

var service = ['users', 'news'],
    lastSync = {
        'users' : false,
        'news' : false
    };
db.transaction(function (tx) {
    return service.reduce(function(promise, serviceItem) {
        return promise.then(function() {
            return executeSqlPromisified(tx, serviceItem).then(function(fTime) {
                if(fTime !== null) {
                    lastSync[serviceItem] = fTime;
                }
            });
        });
    }, $q.when(null)).then(function() {
        console.log(lastSync);
    });
});

function executeSqlPromisified(tx, serviceItem) {
    var deferred = $q.defer();
    tx.executeSql("SELECT time FROM last_sync WHERE fService = ? ORDER BY id DESC LIMIT 1", [serviceItem], function (tx, result) {
        if (result.rows.length) {
            deferred.resolve(result.rows.item(0).fTime);
        } else {
            deferred.resolve(null);
        }
    });
    return deferred.promise;
}

对于未经训练的人来说,这将是非常难以理解的,但相信我,你可以习惯这种模式。