【问题标题】:Recursive function with defer带延迟的递归函数
【发布时间】:2014-11-25 16:49:40
【问题描述】:

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

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;
}

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

【问题讨论】:

  • 您不能在executeSql 回调中使用return。在进入特定于应用程序的过程(如递归调用)之前,适当地抽象回调并使用承诺。

标签: javascript angularjs deferred angular-promise


【解决方案1】:

手动处理多个异步调用并不总是最好的决定。
您可以尝试使用$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), {}))
  • 提供键/值(其中值是承诺)而不是数组映射到 $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,我们可以在那里解决它。

【讨论】:

  • 我从来没有使用过 deferred,可能这个解决方案不是 best pratices ;)
【解决方案2】:

事实上的方法是避免递归,如“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;
}

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

【讨论】:

  • 感谢您的回答,serviceItem 是什么?因为我们的变量中没有serviceItem。以及service 将如何增加?
  • 当我运行此代码时出现错误,错误是:Uncaught TypeError: Cannot read property 'then' of undefined on line return promise.then(function() {
  • serviceItem 只是service 数组的一个元素,即问题代码中称为service[index] 的元素,即'users' 然后'news'。 serviceservice.reduce(...) 构造迭代。 .reduce() 是标准的 javascript 数组方法。
  • 您看到的错误可能是由$q.defer().resolve() 引起的,这是我尝试创建已解决的 $q 承诺。也许$q.defer().resolve(null).promise 会起作用。 Q 的完整实现要简单得多,但 $q 遗漏了很多功能(我对它不太熟悉)。
  • 感谢您的解释,您的意思是我不能在 AngularJS $q 中使用 promise 吗?但我认为问题与promise 有关,因为在return service.reduce(function(promise, serviceItem) { 我们传递了promise 参数,但这个参数之前没有定义。不是吗?
猜你喜欢
  • 2015-09-19
  • 1970-01-01
  • 2012-04-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-15
相关资源
最近更新 更多