我对离子/角度来说是全新的,这是我的代码:
.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);
});
}
})
我在做什么
从服务器获取所有ID
如果db(sqlite)中不存在id
从服务器
将帖子插入db
它最终深深嵌套,丑陋。
这样做的离子角度方法是什么?
答案 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 )。