angularjs - How to avoid nested call in ionic/angular? -
i'm totally new ionic/angular, code:
.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 posts 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 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); }); } }) what doing
get ids server
if id doesnt exist in db(sqlite)
get post id server
insert post db
it ends nested, ugly.
what ionic, angular way this?
as others suggested best option use promises don't have nest statements you're doing.
angularjs uses $q promises:
a service helps run functions asynchronously, , use return values (or exceptions) when done processing.
on internet there tons of articles promises , how chain them.
found article explains common mistakes promises.
it's worth reading cause goes deep topic.
in angularjs create promise using $q service:
function dosomething() { var deferred = $q.defer(); deferred.resolve({value: true}); return deferred.promise; } this bit of code returns promise resolved - since there's no async operation - when it's called. return object property value = true.
cool thing promises fact can chain them:
dosomething() .then(function(result){ // result.value should true. return dosomething(); }) .then(function(result){ // result.value should true. // result of second call. }); passing result of previous - resolved - promise.
if promises rejected because of exceptions:
deferred.reject({value: false}); you can trap error , stop execution in chain:
dosomething() .then(function(result){ // result.value should true. return dosomething(); }) .then(function(result){ // result.value should true. // result of second call. }) .catch(function(reason){ // reason failure. }); finally can use finally cleanup or other things:
dosomething() .then(function(result){ // result.value should true. return dosomething(); }) .then(function(result){ // result.value should true. // result of second call. }) .catch(function(reason){ // reason failure. }) .finally(function(){ // it's going executed @ end of chain, in case of error trapped catch. }); things not simple, though. @ beginning might find spending few hours debugging code.
how fix code ?
first of create function fetch ids calling web api:
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; } as can see i've implemented system described above. $http returns promise wrapped creating new promise, anyway.
then have query database find non existing ids (i didn't put code in loop easier records in 1 call):
function queryforids(ids) { console.log('querying ids ' + ids.tostring() + ' ...'); var deferred = $q.defer(); var params = []; (var = 0; < ids.length; i++) { params.push('?'); } window.mydatabase.transaction(function(tx) { tx.executesql("select * posts postid in (" + params.join(',') + ")", ids, function(tx, results) { deferred.resolve(results.rows); }, function(tx, reason) { deferred.reject(reason); }); }); return deferred.promise; } my code going different i've used websql cause wanted test in browser.
now need find ids not exist in db:
function getnonexistingids(ids, dbdata) { console.log('checking if ids ' + ids.tostring() + ' exist in db ...'); if (!ids || ids.length === 0) { console.log('no ids'); return []; } if (!dbdata || dbdata.length === 0) { console.log('database 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; } this function not return promise still can pipe real promise (you'll find out how later).
now need call web api fetch posts ids couldn't found in database:
function fetchnonexisting(ids) { if (!ids || ids.length === 0) { console.log('no posts fetch!'); return; } console.log('fetching non existing posts 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); } things here interesting.
since want function return 1 , result array of posts i've created array of promises. $http service returns promise. push in array.
@ end try resolve array of promises $q.all. cool!
now need write posts fetched in database.
function writeposts(posts) { if (!posts || posts.length === 0) { console.log('no posts write database!'); return false; } console.log('writing posts ...'); var promises = []; angular.foreach(posts, function(post, key) { promises.push(writepost(post.data)); }); return $q.all(promises); } again, chaining array of promises can resolve them in 1 go. function here calls writepost:
function writepost(post) { return $q(function(resolve, reject) { window.mydatabase.transaction(function(tx) { tx.executesql("insert 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); }); }); }); } this bit here quite complicated cause websql doesn't work promises , want them resolve in 1 go , result back.
now can these functions? well, can chain them explained earlier:
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() { // executed. }); the final result can find found in gist.
if prefer download whole application , test on pc, link (myapp).
Comments
Post a Comment