正如其他人所建议的那样,最好的选择是使用 Promise,这样您就不必像现在这样嵌套语句。
AngularJs 使用 $q promises:
帮助您异步运行函数并使用它们的服务
完成处理后返回值(或异常)。
互联网上有大量关于 Promise 以及如何链接它们的文章。
最近我发现了这个article,它解释了 promise 的常见错误。
值得一读,因为它深入主题。
在 AngularJs 中,您将使用 $q 服务创建一个承诺:
function doSomething() {
var deferred = $q.defer();
deferred.resolve({value: true});
return deferred.promise;
}
这段代码返回一个已解决的承诺 - 因为没有异步操作 - 当它被调用时。它将返回一个具有属性value = true 的对象。
Promise 最酷的地方在于您可以将它们链接起来:
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.
});
传递上一个 - 已解决 - 承诺的结果。
如果 promise 因为某些异常而被拒绝:
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 已经返回了一个 Promise,但我还是将它包装成一个新的 Promise。
然后我将不得不查询数据库以找到不存在的 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;
}
这个函数不会返回一个 Promise,但你仍然可以像处理一个真正的 Promise 一样通过管道传递它(稍后你会知道如何)。
现在我们需要调用 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);
}
这里的事情变得有趣了。
由于我希望此函数返回一个且仅返回一组帖子的结果,因此我创建了一组承诺。
$http 服务已经返回了一个承诺。我将它推入一个数组中。
最后,我尝试使用$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);
}
再一次,我们链接了一系列 Promise,以便我们可以一次性解决所有问题。
这个函数在这里调用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 不适用于 Promise,我希望它们一次性解决并取回结果。
现在您可以使用所有这些功能做什么?好吧,你可以像我之前解释的那样链接它们:
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)。