deferred promise value not updating/resolving/deferring - javascript

I have a controller function that creates something. When the function is called, a setInterval run to get the status of the item.
Here is the service:
(function () {
'use strict';
function myService($q) {
let deferred = $q.defer();
function createSomething(name) {
Meteor.call('createSomething', name, (err, res) {
if (err) {
deferred.reject(err);
} else {
//value returned is the created item (id, name, status)
deferred.resolve(res);
}
});
return deferred.promise;
}
function getStatus(id) {
Meteor.call('getStatus', id, (err, res) {
if (err) {
deferred.reject(err);
} else {
//statuses are queued, processing, created
deferred.resolve(res);
}
});
return deferred.promise;
}
return {
createSomething: createSomething,
getStatus: getStatus
}
}
angular.module('myApp').factory('myService', myService);
})();
And here is the controller:
(function () {
'use strict';
function myController($scope, myService) {
let ctrl = this;
ctrl.create = (name) => {
myService.createSomething(name)
.then((item) => {
ctrl.statusInterval = setInterval(() => {
myService.getStatus(item.data.id)
.then((status) => {
//status is always 'queued' :(
if (status.data.status === 'created') {
clearInterval(ctrl.statusInterval);
//do something
}
});
}, 5000);
});
};
}
angular.module('myApp').controller('myController', myController);
})();
When I check the value of the response in getStatus of the service, the status changes every time it is called (queue -> processing ... processing -> created). However, the value of status in the controller is always queue.
How do I get the promise value to resolve?

createSomething() and getStatus() need to create and return their own promise. They can't share a promise and work properly in all cases.
In addition, they should create and return a unique promise each time they are called, not the same promise every time they are called. Remember, promises are one-way state machines. Once resolved or rejected, their state never changes, even if resolve() or reject() is called again on them.
Here's an example:
function createSomething(name) {
// create a unique deferred inside this function each time you call it
let deferred = $q.defer();
Meteor.call('createSomething', name, (err, res) {
if (err) {
deferred.reject(err);
} else {
//value returned is the created item (id, name, status)
deferred.resolve(res);
}
});
return deferred.promise;
}

Related

Javascript Promise undefined

I am new to Javascript and AngularJS, I am trying to figure this out.
I made an angular service that execute DB queries and return promise.
executeStatement = function(db, sql, values, onsuccess, onerror) {
if (!!db.executeSql) {
return db.executeSql(sql, values || [], onsuccess, onerror);
} else {
return db.transaction(function(tx) {
return tx.executeSql(sql, values, function(ignored, rs) {
return onsuccess(rs);
}, function(ignored, error) {
return onerror(error);
});
});
}
};
this.executeStatement = function(sql, values) {
$ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
return executeStatement(myDB, sql, values, resolve, reject);
});
} );
}
And then If I call the executeStatement like this in the angular service module.
this.extract = function(callback) {
_DB.executeStatement('SELECT * FROM FRIDGE', []).then(callback);}
Shouldn't this return a promise after the functions are finished running?
I'm getting undefined :(
Some help would be appreciated!
The this.executeStatement() function needs to return the result of $ionicPlatform.ready():
this.executeStatement = function(sql, values) {
return $ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
return executeStatement(myDB, sql, values, resolve, reject);
});
} );
}
Now you can access whatever's returned from executeStatement in your callback, which you can pass in as the argument to the .then() function:
_DB.executeStatement('SELECT * FROM FRIDGE', []).then(callback);
The functions within executeStatement are not Promise, do not need to be returned, where onFulfilled, onRejected of Promise constructor is passed as parameters to a function, if and when onsuccess, onerror are called, the original Promise will be resolved or rejected.
Include second parameter to chained .then() or use .catch() to handle potential errors within Promise chain.
Not sure if $ionicPlatform.ready returns a Promise?
_executeStatement = function(db, sql, values, onsuccess, onerror) {
if (!!db.executeSql) {
// `onsuccess` or `onerror` need to be called for `Promise`
// to be fullfiled; a value needs to be passed to
// `onsuccess` or `onerror` to get the value at `.then()` or `.catch()`
db.executeSql(sql, values || [], onsuccess, onerror);
} else {
db.transaction(function(tx) {
tx.executeSql(sql, values, function(ignored, rs) {
onsuccess(rs);
}, function(ignored, error) {
onerror(error);
});
});
}
};
this.executeStatement = function(sql, values) {
return $ionicPlatform.ready( function() {
return new Promise(function(resolve, reject) {
_executeStatement(myDB, sql, values, resolve, reject);
})
.catch(function(err) {
// handle, pass error here
console.log(err);
return err
})
});
}
this.executeStatement(/* parameters */)
.then(function success(data) {
console.log(data);
}, function err(err) {
console.log(err);
});

Promise does not return the right object

I have a problem with the following chain of promises:
Parse.Cloud.run('cloudlogin', {
fb_accessToken: $localStorage.accessTokenFacebook
, facebookID: FACEBOOKID
}, {
success: function (userdata) {
alert(JSON.stringify(userdata))
$localStorage.username = userdata.username;
$localStorage.password = userdata.password;
$localStorage.fb_access_token = userdata.fb_accessToken;
var bool = userdata.isnewuser
alert('bool' + bool)
return bool
}
, error: function (error) {
alert(error)
$state.go("login")
.then(function () {
$ionicLoading.hide()
})
}
})
.then(function (isnewuser) {
$localStorage.organizerAccess = true;
alert('fbdata' + JSON.stringify(isnewuser))
})
I would like to make the first promise to return the boolean 'isnewuser' to the second promise but instead the whole 'userdata' object is returned. Any idea?
success is a callback function its return value won't pass to the next then. if you want to pass the bool value to next one need to rewrite code like below.
Parse.Cloud.run('cloudlogin', {
fb_accessToken: $localStorage.accessTokenFacebook
, facebookID: FACEBOOKID
}, {
error: function (error) {
alert(error)
$state.go("login")
.then(function () {
$ionicLoading.hide()
})
}
}).then(function (userdata) {
alert(JSON.stringify(userdata))
$localStorage.username = userdata.username;
$localStorage.password = userdata.password;
$localStorage.fb_access_token = userdata.fb_accessToken;
var bool = userdata.isnewuser
alert('bool' + bool)
return bool
})
.then(function (isnewuser) {
$localStorage.organizerAccess = true;
alert('fbdata' + JSON.stringify(isnewuser))
})
as the documentation says - then(successCallback, [errorCallback], [notifyCallback]) – regardless of when the promise was or will be resolved or rejected, then calls one of the success or error callbacks asynchronously as soon as the result is available. The callbacks are called with a single argument: the result or rejection reason. Additionally, the notify callback may be called zero or more times to provide a progress indication, before the promise is resolved or rejected.
so, instead of taking the returned value then takes the object returned which caused the success: function (userdata) which is userdata
login: function () {
var deferred = $q.defer();
Parse.Cloud.run('cloudlogin', {
fb_accessToken: $localStorage.accessTokenFacebook,
facebookID: FACEBOOKID
}).error(function (error) {
alert(error)
$state.go("login")
.then(function () {
$ionicLoading.hide()
})
})
.then(function (userdata) {
alert(JSON.stringify(userdata))
$localStorage.username = userdata.username;
$localStorage.password = userdata.password;
$localStorage.fb_access_token = userdata.fb_accessToken;
deferred.resolve(userdata.isnewuser);
});
return deferred.promise;
}
write this function in service class and call it from controller

How to wrap a callback function in a Parse.Promise

I have a function:
function foo(aString, function(err, callback) {
//does stuff
}
I need to call that function from the middle of a long series of Parse.Promises.
How can I wrap it in a Parse.Promise?
I've tried:
//...
return new Parse.Promise(function(resolve, reject) {
foo(thatString, function(err, data) {
if(err) return reject(err);
resolve(data);
});
});
}).then(function(data) {
//...
and other variations like Parse.Promise.as() instead of new Parse.Promise()
Any help would be appreciated.
You're on the right track, but Parse's promise constructor doesn't take resolve / reject functions. Instead, have your wrapper build and return a promise without params, then invoke that promise's resolve() or reject() methods in the wrapped function's callback, like this:
var wrappedFoo = function(aString) {
var promise = new Parse.Promise();
foo(aString, function(err, data) {
if (err) { promise.reject(err); }
promise.resolve(data);
});
return promise;
};
Call it like this:
wrappedFoo("bar").then(function(data) {
// data will be the data passed to the foo callback
}, function(err) {
// likewise for err
});

Recursively calling asynchronous function that returns a promise

I'm trying to recursively call AWS's SNS listEndpointsByPlatformApplication. This returns the first 100 endpoints then a token in NextToken if there are more to return (details: AWS SNS listEndpointsByPlatformApplication).
Here's what I've tried:
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
return reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
return getEndpoints(platformARN, data.NextToken);
}
else {
console.log('trying to break out!');
return resolve(true);
}
}
});
});
}
I'm calling it with:
getEndpoints(platformARNDev, null)
.then(function(ret) {
console.log('HERE!');
}, function(err) {
console.log(err);
});
Problem is: the first call happens, then the recursive call happens, and I get the message trying to break out! but the HERE! never gets called. I've got something wrong with how my promises are returning I think.
Grateful for pointers.
The problem is that you try and resolve/reject partially completed query. Here is a complete working example with dummy service. I incapsulated the data grabbing into it's own recursive function and only do resolve/reject when i've completely fetched all the data or stumbled upon an error:
// This is the mock of the service. It yields data and token if
// it has more data to show. Otherwise data and null as a token.
var dummyData = [0, 1, 2, 3, 4];
function dummyAsyncCall(token, callback) {
token = token || 0;
setTimeout(function() {
callback({
dummyDataPart: dummyData[token],
token: (typeof (dummyData[token]) == 'undefined') ? null : (token + 1)
});
});
}
// Here is how you would recursively call it with promises:
function getAllData() {
//data accumulator is sitting within the function so it doesn't pollute the global namespace.
var dataSoFar = [];
function recursiveCall(token, resolve, reject) {
dummyAsyncCall(token, function(data) {
if (data.error) {
reject(data.error);
}
if (!data.token) {
//You don't need to return the resolve/reject result.
resolve(dataSoFar);
} else {
dataSoFar = dataSoFar.concat(data.dummyDataPart);
recursiveCall(data.token, resolve, reject);
}
});
}
return new Promise(function(resolve, reject) {
// Note me passing resolve and reject into the recursive call.
// I like it this way but you can just store them within the closure for
// later use
recursiveCall(null, resolve, reject);
});
}
//Here is the call to the recursive service.
getAllData().then(function(data) {
console.log(data);
});
Fiddle with me
That's because you dont need to return resolve/reject, just call resolve/reject when the recursive call completes. A rough code would look like this
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
getEndpoints(platformARN, data.NextToken).then(function () {
resolve(true);
}).catch(function (err) {
reject(err);
});
}
else {
console.log('trying to break out!');
resolve(true);
}
}
});
});
}
(caution: this is just a rough code, may work or may not, but is to give a general idea)
I've added a code snippet below, to support this concept, and it works great, check it out.
i = 0;
$('#output').empty();
function pro() {
return new Promise(function(resolve, reject) {
if (i > 3) {
resolve();
return;
}
window.setTimeout(function() {
console.log(i);
$('#output').append(i).append('<br/>');
i += 1;
pro().then(function() {
resolve()
}).catch(function() {
reject()
});
}, 2000);
});
}
pro().then(function () { $('#output').append("now here"); })
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="output"></div>

service returns promise but cachingService not

I am calling the getWeeklyDates which is calling the cachingGlobalConfigurationService which is again calling the globalConfigurationService if the globalConfiguration data could not be found in the localstorage.
The code =>
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
works fine when the globalConfiguration is not cached yet because then I make the ajax call and return a promise.
But the above line of code with .then(function(response1) is undefined when my globalConfiguration can be found in the localStorage and just this is returned:
else {
return cachedGlobalConfiguration;
}
I guess I can not use .then in this case but I would like.
How can I fix that?
1
this.getWeeklyDates= function (projectId, currentDate) {
return cachingGlobalConfigurationService.getGlobalConfiguration()
.then(function(response1){
// do business logic
});
2
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (!cachedGlobalConfiguration) {
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
});
}
else {
return cachedGlobalConfiguration;
}
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: Only save to local storage when service.save was successfully
localStorageService.set('globalConfiguration', globalConfiguration);
globalConfigurationService.saveGlobalConfiguration(globalConfiguration);
}
});
3
'use strict';
angular.module('test').service('globalConfigurationService', function ($http) {
this.getGlobalConfiguration = function () {
// TODO get from db
var path = 'scripts/model/globalConfiguration.json';
return $http.get(path).then(function (response) {
return response.data.globalConfiguration;
});
};
this.saveGlobalConfiguration = function (globalConfiguration) {
// TODO: save on db
//var path = 'scripts/model/globalConfiguration.json';
//return $http.post(path, globalConfiguration).then(function (response) {
// alert('global configuration was saved succesfully!');
//});
}
});
You can inject $q service and use $q.when to wrap the object while returning, so that way you are always returning a promise from your api (and just removed the redundant else). Also remember to reject the promise from catch callback of the promise (if required).
'use strict';
angular.module('test').service('cachingGlobalConfigurationService', function (localStorageService, globalConfigurationService, $q) {
this.getGlobalConfiguration = function () {
var cachedGlobalConfiguration = localStorageService.get('globalConfiguration');
if (cachedGlobalConfiguration) {
//Return a promise
return $q.when(cachedGlobalConfiguration);
}
return globalConfigurationService.getGlobalConfiguration().then(
function (globalConfiguration) {
localStorageService.set('globalConfiguration', globalConfiguration);
return globalConfiguration;
},
function (error) {
console.log('error', error);
return $q.reject(error); //<-- reject
});
};
//....
});
$q.when - Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.

Categories