如何避免在ionic / angular中嵌套调用?

时间:2015-07-08 13:17:49

标签: angularjs ionic-framework

我对离子/角度来说是全新的,这是我的代码:

.controller('PostCtrl', function($scope, Posts, $cordovaSQLite, $http) {
  $scope.getPosts = function() {
    $http.get('http://localhost/postIds').then(function(resp) {
      _.each(resp.data, function(id) {
        var query = "SELECT id FROM posts WHERE id = ?";
        $cordovaSQLite.execute(db, query, [id]).then(function(res) {
          if(res.rows.length = 0) {
            $http.get('http://localhost/post/' + id).then(function(resp) {
              var post = resp.data;
              var query = "INSERT INTO posts (postId, title, user, content) VALUES (?,?,?,?)";
              $cordovaSQLite.execute(db, query, [post.id, post.title, post.user, post.content]).then(function(res) {
                // success
              }, function(err) {
                console.log(err);
              });
            }, function(err) {
              console.log(err);
            });
          }
        }, function (err) {
          console.error(err);
        });
      });
    }, function(err) {
      console.log(err);
    });
  }
})

我在做什么

  1. 从服务器获取所有ID

  2. 如果db(sqlite)中不存在id

  3. 从服务器

  4. 获取ID
  5. 将帖子插入db

  6. 它最终深深嵌套,丑陋。

    这样做的离子角度方法是什么?

1 个答案:

答案 0 :(得分:3)

正如其他人所建议的那样,最好的选择是使用承诺,这样你就不必像你一样嵌套陈述。

AngularJs使用$ q promises

  

一种帮助您异步运行函数并使用它们的服务   完成处理后返回值(或例外)。

在互联网上有大量关于承诺以及如何链接它们的文章 最近我发现这个article解释了承诺中常见的错误 它值得一读,因为它深入到了这个主题。

在AngularJs中,您可以使用$q服务创建承诺:

function doSomething() {
   var deferred = $q.defer();
   deferred.resolve({value: true});
   return deferred.promise;
}

这段代码返回一个已解决的承诺 - 因为它没有异步操作 - 当它被调用时。它将返回一个属性为value = true的对象 关于承诺的一件很酷的事情是你可以将它们链接起来:

doSomething()
  .then(function(result){
      // result.value should be true.
      return doSomething();
  })
  .then(function(result){
      // result.value should be true.
      // this is the result of the second call.
  });

传递上一个 - 已解决 - 承诺的结果。

如果承诺因某些例外而被拒绝:

deferred.reject({value: false});

您可以捕获错误并停止链中的执行:

doSomething()
  .then(function(result){
      // result.value should be true.
      return doSomething();
  })
  .then(function(result){
      // result.value should be true.
      // this is the result of the second call.
  })
  .catch(function(reason){
      // reason for failure.
  });

最后,您可以使用finally进行一些清理或其他事情:

doSomething()
  .then(function(result){
      // result.value should be true.
      return doSomething();
  })
  .then(function(result){
      // result.value should be true.
      // this is the result of the second call.
  })
  .catch(function(reason){
      // reason for failure.
  })
  .finally(function(){
      // it's going to be executed at the end of the chain, even in case of error trapped by the catch.
  });
但是,事情并非如此简单。一开始你可能会发现自己花了几个小时来调试代码。

我如何修复您的代码?

首先,我将创建一个函数来获取调用web api的ID:

function fetchIds() {

    console.log('Fetching Ids ...');

    var deferred = $q.defer();

    $http({
        method: 'GET',
        url: 'http://localhost/postIds',
        params: {}
    })
    .success(function(data) {
        deferred.resolve(data);
    })
    .error(function(data, status) {
        deferred.reject(data);
    });

    return deferred.promise;
}

如您所见,我已实施上述系统。 $http已经返回了一个承诺,但无论如何我将它包裹起来创建一个新的承诺。

然后我将不得不查询数据库以找到不存在的id(我没有将我的代码放在循环中,因为在一次调用中更容易获取所有记录):

function queryForIds(ids) {

    console.log('Querying for Ids ' + ids.toString() + ' ...');

    var deferred = $q.defer();

    var params = [];
    for (var i = 0; i < ids.length; i++) {
        params.push('?');
    }

    window.myDatabase.transaction(function(tx) {
       tx.executeSql("SELECT * FROM posts WHERE postId IN (" + params.join(',') + ")", ids,
           function(tx, results) {
              deferred.resolve(results.rows);
           },
           function(tx, reason) {
              deferred.reject(reason);
           });
    });

    return deferred.promise;
}

我的代码与您使用的WebSql略有不同,因为我想在浏览器中测试它。

现在我们需要找到db中不存在的ID:

function getNonExistingIds(ids, dbData) {

    console.log('Checking if Ids ' + ids.toString() + ' exist in the db ...');

    if (!ids || ids.length === 0) {
        console.log('No ids');
        return [];
    }

    if (!dbData || dbData.length === 0) {
        console.log('database is empty');
        return ids;
    }

    var dbIds = [];
    angular.forEach(dbData, function(data, key) {
        dbIds.push(data.postId);
    });

    var nonExisting = [];

    angular.forEach(ids, function(id, key) {
        var found = $filter('filter')(dbIds, id, true);
        if (found.length === 0) {
            nonExisting.push(id);
        }
    });

    return nonExisting;
}

这个函数不会返回一个承诺,但是你仍然可以像管理一个真正的承诺一样管道它(你以后会发现它如何)。

现在我们需要调用web api来获取无法在数据库中找到的id的帖子:

function fetchNonExisting(ids) {

    if (!ids || ids.length === 0) {
        console.log('No posts to fetch!');
        return;
    }

    console.log('Fetching non existing posts by id: ' + ids.toString() + ' ...');

    var promises = [];

    angular.forEach(ids, function(id, key) {
        var promise = $http({
            method: 'GET',
            url: 'http://localhost/post/' + id,
            params: {}
        });
        promises.push(promise);
    });

    return $q.all(promises);
}

这里的事情很有趣。

由于我希望这个函数返回一个且只有一个帖子数组的结果,我已经创建了一系列的promises。 $ http服务已经返回一个promise。我将它推入阵列 最后,我尝试使用$q.all解决承诺数组。真的很酷!

现在我们需要编写数据库中提取的帖子。

function writePosts(posts) {

    if (!posts || posts.length === 0)
    {
        console.log('No posts to write to database!');
        return false;
    }

    console.log('Writing posts ...');

    var promises = [];

    angular.forEach(posts, function(post, key) {
        promises.push(writePost(post.data));
    });

    return $q.all(promises);
}

同样,我们正在链接一系列承诺,以便我们可以一次性解决所有问题。 此功能在此处调用writePost

function writePost(post) {
    return $q(function(resolve, reject) {
        window.myDatabase.transaction(function(tx) {
            tx.executeSql("INSERT INTO posts (postId, title, user, content) VALUES (?,?,?,?)", [post.id, post.title, post.user, post.content],
                function(tx, result) {
                    console.log('INSERT result: ' + result);
                    resolve(result);
                },
                function(tx, reason) {
                    console.log('INSERT failure: ' + reason);
                    reject(reason);
                });
        });
    });
}

这一点非常复杂,因为WebSql不能使用promises,我希望它们能够一次性解决并获得结果。

现在你能用所有这些功能做什么?好吧,你可以像我之前解释的那样链接它们:

var ids = [];

fetchIds()
    .then(function(data) {
        console.log(data);
        ids = data;
        return queryForIds(data);
    })
    .then(function(dbData) {
        return getNonExistingIds(ids, dbData);
    })
    .then(function(nonExistingIds) {
        console.log('Non existing ids: ' + nonExistingIds);
        return fetchNonExisting(nonExistingIds);
    })
    .then(function(response) {
        return writePosts(response);
    })
    .then(function(result) {
        console.log('final result: ' + result);
    })
    .catch(function(reason) {
        console.log('pipe error: ' + reason);
    })
    .finally(function() {
        // Always Executed.
    });

最终结果可以在gist中找到。

如果您希望下载整个应用程序并在PC上进行测试,则可以使用link myApp )。