I can't figure out why this isn't resolving, any ideas? "resolve this" does print, but it never makes it back to the resolution of the promise in .then.
var promise = wait();
promise.then(function(result){
console.log("wait returned - " + result);
});
function wait(){
var deferred = $q.defer();
if(busy){
setTimeout(function(){
wait();
},500);
} else {
console.log("resolve this");
deferred.resolve("Wait is over.");
}
return deferred.promise;
};
Here's how it can be done instead:
var promise = wait();
promise.then(function(result){
console.log("wait returned - " + result);
});
function wait(){
var deferred = $q.defer();
(function _wait() {
if (busy) {
setTimeout(_wait, 500);
} else {
console.log("resolve this");
deferred.resolve("Wait is over.");
}
})();
return deferred.promise;
};
The key difference is that there will be only one deferred, created and returned by a 'wrapper' function. This deferred will be eventually resolved by _wait function.
In your case, each subsequent (recursive) wait() call creates a different deferred object. One of these objects will be eventually resolved, right - but that will be the same object as returned by the first wait() call only if busy will be false at this moment. Apparently, most of the time it won't.
Each time you call wait, it makes a new promise. The calls to wait inside your setTimeout function do nothing to the first promise created. Try this instead:
var promise = wait();
promise.then(function(result){
console.log("wait returned - " + result);
});
function wait(){
var deferred = $q.defer();
var timer = setInterval(function() {
if(!busy) {
clearInterval(timer);
console.log("resolve this");
deferred.resolve("Wait is over.");
}
}, 500);
return deferred.promise;
};
Also, depending on how the rest of your program is structured, it may be a good idea to resolve the promise as soon as busy becomes true; then you don't have to wait as long.
Related
I have the following code
var pOne = new Promise(function(callback){
setTimeout(function(){
callback(false);
}, 100);
}).then(function(v){
console.log("pOne: " + v);
});
var pTwo = new Promise(function(callback){
setTimeout(function(){
callback(true);
}, 100);
}).then(function(v){
console.log("pTwo: " + v);
});
Promise.all([pOne, pTwo]).then(function(values){
console.log(values);
});
The console.log(values) displays [undefined, undefined] in the console. My understanding of promises is that I should be able to chain the then() method. Does chaining not work with the Promise.all() or is this a bug.
Note: I am using the promise-polyfill but running it on chrome, so it technically it is using native chrome implementation of promises.
your pOne and pTwo promises don't return anything.
Try this:
var pOne = new Promise(function(callback){
setTimeout(callback, 100, true);
}).then(function(v){
return v;
});
var pTwo = new Promise(function(callback){
setTimeout(callback, 100, false);
}).then(function(v){
return v;
});
Promise.all([pOne, pTwo]).then(function(values){
console.log(values);
});
pOne and pTwo have to resolve with a value in order for that value to be passed to the result of Promise.all.
var pOne = new Promise(function(callback){
setTimeout(function(){
callback(false);
}, 100);
}).then(function(v){
console.log("pOne: " + v);
return v;
});
Notice the return v inside of the .then callback. That means that the pOne promise is going to resolve with that value (v in this case being whatever the previous Promise resolved with, or in this case, false.
Now do the same for the pTwo promise.
var pTwo = new Promise(function(callback){
setTimeout(function(){
callback(true);
}, 100);
}).then(function(v){
console.log("pTwo: " + v);
return v;
});
Again, we have to return a value from the .then callback function in order for the Promise to resolve with a value, rather than with undefined.
Now, Promise.all is going to run the Promises, and when (or if) they resolve (in our case they always do), it's going to get the resolved value from each one, and the Promise.all promise itself is going to resolve with the values.
I have a service where I exchange data, in this service I keep a promise created by $interval, nothing fancy:
$rootScope.recursivePosition = null;
$rootScope.trackMe = function(){
var extensionName = $state.current.extensionName;
if ($rootScope.tracking === false) {
$rootScope.tracking = true;
$rootScope.recursivePosition = $interval(function(){
someService.getAllPositions($rootScope.content[extensionName]);
}, 2000);
} else {
$interval.cancel($rootScope.recursivePosition);
console.log("recursivePosition cancel");
console.dir($rootScope.recursivePosition);
$rootScope.tracking = false;
}
};
The thing is, inside that service I have another promise (from $cordovaGeolocation) when I cancel the first promise ($rootScope.recursivePosition) it still works for a while, like 4 seconds more. Can I control this behavior ?
A promise inside a promise can't be cancelled. But a promise derived from that promise can be cancelled.
function getAllPositions(x) {
var defer = $q.defer();
var derivedPromise = defer.promise;
derivedPromise.cancel = function () {
defer.reject('cancelled');
});
$cordovaGeolocation(x)
.then(function onSuccess(value) {
defer.resolve(value);
}).catch(function onReject(error) {
defer.reject(error);
});
return derivedPromise;
};
The above example returns a promise derived from the $cordovaGeolocation promise. Attached to it is a method named cancel which rejects the promise if called before $cordovaGeolocation resolves.
Asynchronous operations started by $cordovaGeolocation can't be stopped but operations chained from its promise can be cancelled.
In the below code, I want sequential executuon of the method saveBulkUploadSinglePacket in the while loop, that means process next packet after completion of the current packet. How to achieve that.
var saveBulkUploadSinglePacket = function(){
while (packetCount<=bulkUploadPackets.length){
$.when(saveBulkUploadSinglePacket(modelToSave)).done(function(arguments){
saveBulkUploadPackets.push(arguments);
packetCount++;
});
}
return saveBulkUploadPackets;
}
var saveBulkUploadSinglePacket = function(modelToSave){
var defer = $.Deferred();
$.when(new SaveBulkUpload().save(modelToSave)).done(function(arguments){
defer.resolve(arguments);
}).fail(function(errorObj){
defer.reject(errorObj);
});
return defer.promise();
}
The standard way to say "perform x when promise is done" is through promise.then(). Keep track of the current promise in a var outside the loop and attach each call to the previous promise with a .then():
var saveBulkUploadSinglePacket = function(){
var lastPromise = $.when();
while (packetCount<=bulkUploadPackets.length){
lastPromise = (
lastPromise
.then(function(){
// make sure saveBulkUploadSinglePacket returns a promise
return saveBulkUploadSinglePacket(modelToSave));
})
.then(function(){
saveBulkUploadPackets.push(arguments);
packetCount++;
// return a resolved promise
return $.when();
})
);
}
lastPromise.resolve(saveBulkUploadPackets);
return lastPromise;
}
At the end of the function I resolved the final promise with the desired return value, then returned the promise. This way you can call saveBulUploadSinglePacket().then(...) to wait for all the promises to complete and handle the result.
To save your pakets sequentially, use Array.prototype.reduce() to build a .then() chain very concisely, as follows :
var saveBulkUploadPackets = function(packets) {
return packets.reduce(function(promise, paket) {
return promise.then(function(results) {
return (new SaveBulkUpload()).save(paket).then(function(res) {
return results.concat(res);
});
});
}, $.when([]));
}
And call like this :
saveBulkUploadPackets(bulkUploadPackets).then(function(results) {
// All done.
// use/log the `results` array here.
});
New to the JavaScript Promises and having tough time grasping the concept. It seemed I finally understood, but can't seem to get it working.
Here's a simple try:
first = function(){
var deferred = new $.Deferred();
console.log("first running")
return deferred.promise();
}
second = function(){
console.log("second running..sigh..");
}
$(document).ready(function() {
first().then(second);
});
Second is not being called.
In order for the second function to be called, you need to resolve the deferred returned from the first function:
first = function(){
var deferred = new $.Deferred();
console.log("first running");
deferred.resolve(); // <----------resolve the deferred
return deferred.promise();
}
You can also resolve it with arguments so that whatever its resolved with, will be passed as arguments to your second function. Here's a fiddle that adds a slight delay to the resolve so it mimics asynchronous behavior and resolves with actual data:
http://jsfiddle.net/1k6tLev8/1/
You can think a Promise as a task that, in future, will be processed and a result will be returned to all functions that follow the deferred object.
Promises can have 3 + 1 states:
Pending (The task isn't processed yet)
FullFilled or Resolved (Correctly Processed)
Rejected (Processed but failed)
Settled (indicates that the task is already processed.)
var doSomethingAsync = new Promise(function(resolve, reject) {
window.setTimeout(function() {
resolve('Hello World');
// OR
// reject('You Are Not Welcome')
}, 5000);
});
doSomethingAsync.then(
function(message) {
console.log('After few seconds we can finally tell you:', message)
},
function(error) {
console.log('After few seconds we can finally tell you that: ', error);
}
);
As you can see in the above snippet the then method of a Promise Object accepts TWO params (note, when available, there is a third parameter called notify or progress), the first is called in case of fullfilment, the second in case of rejection.
While the promise is in Pending no callbacks are called!
I want to use $q.when() to wrap some non-promise callbacks. But, I can't figure out how to resolve the promise from within the callback. What do I do inside the anonymous function to force $q.when() to resolve with my reason?
promises = $q.when(
notAPromise(
// this resolves the promise, but does not pass the return value vvv
function success(res) { return "Special reason"; },
function failure(res) { return $q.reject('failure'); }
)
);
promises.then(
// I want success == "Special reason" from ^^^
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
The functionality I want to duplicate is this:
promises = function(){
var deferred = $q.defer();
notAPromise(
function success(res) { deferred.resolve("Special reason"); },
function failure(res) { deferred.reject('failure'); }
);
return deferred.promise;
};
promises.then(
// success == "Special reason"
function(success){ console.log("Success: " + success); },
function(failure){ console.log("I can reject easily enough"); }
);
This is good, but when() looks so nice. I just can't pass the resolve message to then().
UPDATE
There are better, more robust ways to do this. $q throws exceptions synchronously, and as #Benjamin points out, the major promise libs are moving toward using full Promises in place of Deferreds.
That said, this question is looking for a way to do this using $q's when() function. Objectively superior techniques are of course welcome but don't answer this specific question.
The core problem
You're basically trying to convert an existing callback API to promises. In Angular $q.when is used for promise aggregation, and for thenable assimilation (that is, working with another promise library). Fear not, as what you want is perfectly doable without the cruft of a manual deferred each time.
Deferred objects, and the promise constructor
Sadly, with Angular 1.x you're stuck with the outdated deferred interface, that not only like you said is ugly, it's also unsafe (it's risky and throws synchronously).
What you'd like is called the promise constructor, it's what all implementations (Bluebird, Q, When, RSVP, native promises, etc) are switching to since it's nicer and safer.
Here is how your method would look with native promises:
var promise = new Promise(function(resolve,reject){
notAPromise(
function success(res) { resolve("Special reason") },
function failure(res) { reject(new Error('failure')); } // Always reject
) // with errors!
);
You can replicate this functionality in $q of course:
function resolver(handler){
try {
var d = $q.defer();
handler(function(v){ d.resolve(v); }, function(r){ d.reject(r); });
return d.promise;
} catch (e) {
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
}
Which would let you do:
var promise = resolver(function(resolve,reject){
notAPromise(function success(res){ resolve("Special reason"),
function failure(res){ reject(new Error("failure")); })
});
promise.then(function(){
});
An automatic promisification helper
Of course, it's equally easy to write an automatic promisification method for your specific case. If you work with a lot of APIs with the callback convention fn(onSuccess, onError) you can do:
function promisify(fn){
return function promisified(){
var args = Array(arguments.length + 2);
for(var i = 0; i < arguments.length; i++){
args.push(arguments[i]);
}
var d = $q.defer();
args.push(function(r){ d.resolve(r); });
args.push(function(r){ d.reject(r); });
try{
fn.call(this, args); // call with the arguments
} catch (e){ // promise returning functions must NEVER sync throw
return $q.reject(e);
// $exceptionHandler call might be useful here, since it's a throw
}
return d.promise; // return a promise on the API.
};
}
This would let you do:
var aPromise = promisify(notAPromise);
var promise = aPromise.then(function(val){
// access res here
return "special reason";
}).catch(function(e){
// access rejection value here
return $q.reject(new Error("failure"));
});
Which is even neater
Ok here's my interpretation of what I think you want.
I am assuming you want to integrate non-promise callbacks with a deferred/promise?
The following example uses the wrapCallback function to wrap two non-promise callbacks, successCallback and errCallback. The non-promise callbacks each return a value, and this value will be used to either resolve or reject the deferred.
I use a random number to determine if the deferred should be resolved or rejected, and it is resolved or rejected with the return value from the non-promise callbacks.
Non angular code:
function printArgs() {
console.log.apply(console, arguments);
}
var printSuccess = printArgs.bind(null, "success");
var printFail = printArgs.bind(null, "fail");
function successCallback() {
console.log("success", this);
return "success-result";
}
function errCallback() {
console.log("err", this);
return "err-result";
}
function wrapCallback(dfd, type, callback, ctx) {
return function () {
var result = callback.apply(ctx || this, arguments);
dfd[type](result);
};
}
Angular code:
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $q) {
var dfd = $q.defer();
var wrappedSuccess = wrapCallback(dfd, "resolve", successCallback);
var wrappedErr = wrapCallback(dfd, "reject", errCallback);
var rnd = Math.random();
var success = (rnd > 0.5);
success ? wrappedSuccess() : wrappedErr();
console.log(rnd, "calling " + (success ? "success" : "err") + " callback");
dfd.promise.then(printSuccess, printFail);
}
Example output where the random number is less than 0.5, and so the deferred was rejected.
err Window /fiddlegrimbo/m2sgu/18/show/
0.11447505658499701 calling err callback
fail err-result