In the following code, attenCalendar.refresh(); method calls a sequence of requests/responses and executes asynchronously. It needs to be finished in order to attenCalendar.getItem be successful. I used promises to solve it, but apparently promise resolves before the refresh completes and causes to attenCalendar.getItem returns null.
How can I block the sequence till refresh() finishes?
yield promise_attendee1_assert();
function attendeeRefresh(){
let deferred = Promise.defer();
attenCalendar.refresh();
deferred.resolve("refreshed");
return deferred.promise;
}
function promise_attendee1_assert(){
let deferred = Promise.defer();
let promise = attendeeRefresh();
promise.then(function onRefreshed(refreshNote) {
dump("refreshedattendee"+refreshNote);
attenCalendar.getItem(attendItem.id, {
onGetResult: function onGetResult(cal, stat, type, detail, count, items) {
retrievedItem = items[0];
//prints null
dump("\nattendeeitem:"+retrievedItem.title);
},
onOperationComplete: function() {
deferred.resolve();
}
});
});
return deferred.promise;
}
Related
I'm having a (seemingly fundamental) problem understanding promises. First the code:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement);
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise("1st statement"),
mockPromise("2nd statement"),
mockPromise("3rd statement")
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Each promise function gets invoked upon adding it to the promise array, as opposed to when Q.all is called as I thought.
What am I not getting here?
How do I queue an array of promises without immediately invoking said promises?
Promises are objects. They are not 'executed'. They are 'resolved' or 'rejected'. When you create the array, you are executing the mockPromise() function three times. This function is immediately executed in that point of the code.
The mockPromise() function creates a deferred and returns the associated promise. It also sets a timer to resolve the returned promise in the future.
Q.all() just waits for the 3 promises to be 'resolved'. (technically it returns a new promise that will be resolved when the 3 previous promises are resolved)
If you want to execute the three async functions one after the other, I would recommend using the excellent async.js library. It provides many async flow control primitives. In your case you may be interested in series or waterfall methods.
It seems the confusion is that you understand the promise API to be designed for lazy evaluation, which is not the case.
Promises are a way of handling long running requests, they were designed to start IMMEDIATELY to minimize waiting time, and to utilize chaining and joining to clarify how the results of these long running requests should be processed.
You might try to utilize the api Q-Lazy which allows you to delay invocation of promises until they have been subscribed to.
You'd normally defer asynchronous functionality, not just a value. For example:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement());
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise(function() {
console.log("running1");
return "1st statement";
}),
mockPromise(function() {
console.log("running2");
return "2nd statement";
}),
mockPromise(function() {
console.log("running3");
return "3rd statement";
}),
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Note that the deferred functionality is going to run regardless of whether you ever call a .then on a promise.
Let me show a sample using standard promises. They work pretty much the same as Q promises:
function mockPromise(value) {
return new Promise(resolve => {
console.log("I'm not running before I'm queued ...");
setTimeout(() => {
resolve(value);
}, 1000);
});
}
mockPromise("1st promise").then(x => {
console.log(x);
return mockPromise("2nd promise");
}).then(x => {
console.log(x);
return mockPromise("3nd promise");
}).then(x => {
console.log(x);
});
So I have a situation where I have multiple promise chains of an unknown length. I want some action to run when all the CHAINS have been processed. Is that even possible? Here is an example:
app.controller('MainCtrl', function($scope, $q, $timeout) {
var one = $q.defer();
var two = $q.defer();
var three = $q.defer();
var all = $q.all([one.promise, two.promise, three.promise]);
all.then(allSuccess);
function success(data) {
console.log(data);
return data + "Chained";
}
function allSuccess(){
console.log("ALL PROMISES RESOLVED")
}
one.promise.then(success).then(success);
two.promise.then(success);
three.promise.then(success).then(success).then(success);
$timeout(function () {
one.resolve("one done");
}, Math.random() * 1000);
$timeout(function () {
two.resolve("two done");
}, Math.random() * 1000);
$timeout(function () {
three.resolve("three done");
}, Math.random() * 1000);
});
In this example, I set up a $q.all() for promises one, two, and three which will get resolved at some random time. I then add promises onto the ends of one and three. I want the all to resolve when all the chains have been resolved. Here is the output when I run this code:
one done
one doneChained
two done
three done
ALL PROMISES RESOLVED
three doneChained
three doneChainedChained
Is there a way to wait for the chains to resolve?
I want the all to resolve when all the chains have been resolved.
Sure, then just pass the promise of each chain into the all() instead of the initial promises:
$q.all([one.promise, two.promise, three.promise]).then(function() {
console.log("ALL INITIAL PROMISES RESOLVED");
});
var onechain = one.promise.then(success).then(success),
twochain = two.promise.then(success),
threechain = three.promise.then(success).then(success).then(success);
$q.all([onechain, twochain, threechain]).then(function() {
console.log("ALL PROMISES RESOLVED");
});
The accepted answer is correct. I would like to provide an example to elaborate it a bit to those who aren't familiar with promise.
Example:
In my example, I need to replace the src attributes of img tags with different mirror urls if available before rendering the content.
var img_tags = content.querySelectorAll('img');
function checkMirrorAvailability(url) {
// blah blah
return promise;
}
function changeSrc(success, y, response) {
if (success === true) {
img_tags[y].setAttribute('src', response.mirror_url);
}
else {
console.log('No mirrors for: ' + img_tags[y].getAttribute('src'));
}
}
var promise_array = [];
for (var y = 0; y < img_tags.length; y++) {
var img_src = img_tags[y].getAttribute('src');
promise_array.push(
checkMirrorAvailability(img_src)
.then(
// a callback function only accept ONE argument.
// Here, we use `.bind` to pass additional arguments to the
// callback function (changeSrc).
// successCallback
changeSrc.bind(null, true, y),
// errorCallback
changeSrc.bind(null, false, y)
)
);
}
$q.all(promise_array)
.then(
function() {
console.log('all promises have returned with either success or failure!');
render(content);
}
// We don't need an errorCallback function here, because above we handled
// all errors.
);
Explanation:
From AngularJS docs:
The then method:
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.
$q.all(promises)
Combines multiple promises into a single promise that is resolved when
all of the input promises are resolved.
The promises param can be an array of promises.
About bind(), More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Recently had this problem but with unkown number of promises.Solved using jQuery.map().
function methodThatChainsPromises(args) {
//var args = [
// 'myArg1',
// 'myArg2',
// 'myArg3',
//];
var deferred = $q.defer();
var chain = args.map(methodThatTakeArgAndReturnsPromise);
$q.all(chain)
.then(function () {
$log.debug('All promises have been resolved.');
deferred.resolve();
})
.catch(function () {
$log.debug('One or more promises failed.');
deferred.reject();
});
return deferred.promise;
}
There is a way. $q.all(...
You can check the below stuffs:
http://jsfiddle.net/ThomasBurleson/QqKuk/
http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/
You can use "await" in an "async function".
app.controller('MainCtrl', async function($scope, $q, $timeout) {
...
var all = await $q.all([one.promise, two.promise, three.promise]);
...
}
NOTE: I'm not 100% sure you can call an async function from a non-async function and have the right results.
That said this wouldn't ever be used on a website. But for load-testing/integration test...maybe.
Example code:
async function waitForIt(printMe) {
console.log(printMe);
console.log("..."+await req());
console.log("Legendary!")
}
function req() {
var promise = new Promise(resolve => {
setTimeout(() => {
resolve("DARY!");
}, 2000);
});
return promise;
}
waitForIt("Legen-Wait For It");
I'm struggling to understand why after calling updateStatus() I see 'promise resolved' logged to the console, but not 'refreshGames'. How is the promise resolved if the code inside refreshGames() never runs?
var refreshGames = function() {
console.log('refreshGames');
var defer = $q.defer();
playersService.getGames({
playerId: playerId
}).$promise.then(function(data) {
vm.games = data;
return defer.promise;
});
};
var updateStatus = function() {
$q.all([refreshGames.promise]).then(function() {
console.log('promise resolved');
populateOptions(vm.games);
vm.tableParams.reload();
});
};
Because your function refreshGames returns nothing, it should return the promise and the defer must be resolved, like this:
var refreshGames = function() {
console.log('refreshGames');
var defer = $q.defer();
playersService.getGames({
playerId: playerId
}).$promise.then(function(data) {
vm.games = data;
defer.resolve(data);
});
return defer.promise;
};
and in the $q.all you just do refreshGames()
refreshGames.promise is undefined - there's no code anywhere that creates this property on refreshGames
any non-promise in $q.all is promisified and effectively equivalent to Promise.resolve(n) (or however you do that with $q
so, your $q.all is essentially
$q.all([undefined]).then(function() {
...
});
and thus gets executed immediately
I call a function that returns a list with ID that i want to use in chained call. Everything seems to work until i want to read all those objects that are returned.. Those are promises but i cannot find out why i cannot resolve them.
//Get bubbles and then it calls another function getBubbleMessage with result from previous and last getBubbleMessage returns an array of promises.
$scope.loadStartPage = function () {
$scope.getBubblesThatUserHasAccessTo().then($scope.getBubbleMessage).then(function (data) {
$log.info("INFO:: " + data);
$scope.bubblesWithMessages = data;
});
};
$scope.getBubblesThatUserHasAccessTo = function () {
var deferred = $q.defer();
BubblesService.getBubblesUserAccess().then(function (result) {
deferred.resolve(result);
});
return deferred.promise;
};
This function is gettings some things that we need to resolve messages connected to those id:s that above service is returning
$scope.getBubblesThatUserHasAccessTo = function () {
var deferred = $q.defer();
BubblesService.getBubblesUserAccess().then(function (result) {
deferred.resolve(result);
});
return deferred.promise;
};
This function get alls messages and returns promise objects - and these i cannot resolve??
$scope.getBubbleMessage = function (data) {
var deferred = $q.defer();
var promises = [];
angular.forEach(data, function (item) {
$log.info("DDD" + item.name);
var promise = BubblesService.getBubbleMessages(item.id, 0, 1);
promises.push(promise);
});
//return $q.all([promises]);
$q.all([promises]).then(function (result) {
$log.info(result);
return result[0];
});
};
Above function returns an array of 60 objects..
In the end i want to have a new object that i use in my ng-repeat on page. I really think this is something todo that im new to angular and promises.... but after a couple of hours of trying to fix this i really need help :)
$q.all takes an array of promise. Here, you are doing $q.all([myPromises]), which resolve instantly, because '[myPromise]' is an array and not a promise (you give an array parameter with first and only element is an array of promise when you should simply use the promise array. So [] and not [[]]). Second issue : you are not returning this promise in the parent function.
You should simply change the block
$q.all([promises]).then(function (result) {
$log.info(result);
return result[0];
});
with
return $q.all(promises);
Which will resolve with an array of resolved for each promise in the array.
I have a test that requires one promise to be run and later in its then handler another promise returning function is run.
The first promise resolves, and a successful call is made to the next function which returns a promise. However, the then handler for that second promise never fires.
Is there another way to test nested promises using Jasmine 2.0?
Example problem:
describe("nested promise suite", function () {
var prom1 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom1');
}, 500)
return deferred.promise;
};
var prom2 = function () {
var deferred = $q.defer();
$timeout(function () {
deferred.resolve('prom2');
}, 500);
return deferred.promise;
};
iit("nested promise test", function (done) {
prom1()
.then(function (result) {
console.log('prom1 result ', result);
prom2()
.then(function (result2) {
console.log('prom2 result ', result2);
})
.finally(function() {
console.log('you did it');
})
})
.finally(done); //this causes promise one to resolve properly but unsure of how to make the second promise resolve properly
$timeout.flush();
})
});
I'm not sure if this is the problem in your original code as well, but in this example, your second console.log doesn't fire because prom2 adds a new timeout after the first has been flushed. The promise from prom2 then waits for this new timeout to flush, which never happens:
prom1() is called
The first promise and timeout are created
The first timeout is flushed, resolving the first promise
The then() block is triggered, calling prom2()
The second promise and timeout are created
done() is called.
You can try adding a second $timeout.flush(); call right after the one that's already there, which will flush the second timeout, resolve the second promise, and log the missing messages.