Ember.RSVP.all seems to resolve immediately - javascript

I'm really hoping that there's something dumb that I'm doing, but I can't seem to find it.
I'm trying to use Ember.RSVP.all in the middle of a chain of promises. The example I have is much simpler than my use, but it demonstrates the issue. In the middle of a chain of promises, I have a set of promises that all need to resolve before the chain can continue - exactly what I understand RSVP.all to be for.
Unfortunately, when I return the RSVP.all object, the next promise in the chain runs immediately, without waiting for the promises passed to all().
I've set up a js fiddle to demonstrate in the best way that I can think of:
http://jsfiddle.net/3a9arbht/3/
Notice that First and Second both resolve at almost exactly the same time, when Second should be after the 1s promise comes back. Third and fourth follow as expected.
Fiddle code looks like this:
function delayAjax(delay) {
return Ember.$.ajax({
url: '/echo/json/',
data: {
json: '',
delay: delay,
}
});
}
delayAjax(1).then(function() {
Ember.$('#first').addClass('red');
var proms = [delayAjax(1), delayAjax(1)];
return Ember.RSVP.all(proms)
}).then(function() {
Ember.$('#second').addClass('red');
return delayAjax(1);
}).then(function() {
Ember.$('#third').addClass('red');
return delayAjax(1);
}).then(function() {
Ember.$('#fourth').addClass('red');
});

Answering based on a response to another question. It appears that while the $.ajax response is indeed "thenable", it is a jQuery deferred object, not a Promise. It's not clear to me why they don't play well together, but the solution is simply to convert the ajax call to a promise:
function delayAjax(delay) {
return Promise.resolve($.ajax({
url: '/echo/json/',
data: {
json: '',
delay: delay,
}
}));
}
With a working fiddle: http://jsfiddle.net/evilbuck/vqut9zy2/3/

Related

How to poll an API as part of a $.Deferred()

The below non-functional example should explain what I'm trying to do, I just don't understand the pattern I need to use to accomplish it. I tried googling to understand polling and deferred, but I couldn't find anything I could understand.
I have a function which polls an API, and I want to wait for that polling to return an expected result (waiting for the endpoint to indicate something has changed) before continuing with my main function. What am I doing wrong?
Edit: I should add that what SEEMS to go wrong with the code below is that even though deferred.resolve() eventually gets called, it seems it isn't the same deferred that got returned, so the when never gets activated in main(). I'm guessing it has to do with the timeout, meaning deferred get clobbered on the first repeat. That is my assumption, anyways.
function pollAPI() {
var deferred = $.Deferred();
$.ajax({
url: url,
contentType: 'application/JSON',
method: 'GET'
}).done(function(data){
if (!desiredResult) {
setTimeout(function() {
pollAPI();
}, 1000);
} else {
deferred.resolve();
}
}).error(deferred.reject());
return deferred.promise();
}
function main() {
$.when(pollAPI()).then(function() {
// do something now that the API has returned the expected result
});
You can use chaining of subsequent calls to the pollAPI() function to create a single promise that has others chained onto it. That would work like this:
// utility function to create a promise that is resolved after a delay
$.promiseDelay = function(t) {
return $.Deferred(function(def) {
setTimeout(def.resolve, t);
}).promise();
}
function pollAPI() {
return $.ajax({
url: url,
contentType: 'application/JSON',
method: 'GET'
}).then(function(data) {
// some logic here to test if we have desired result
if (!desiredResult) {
// chain the next promise onto it after a delay
return $.promiseDelay(1000).then(pollAPI);
} else {
// return resolved value
return someValue;
}
});
}
function main() {
pollAPI().then(function(result) {
// got desired result here
}, function(err) {
// ended with an error here
});
}
This has the following benefits:
No unnecessary promise is created to try to surround the ajax call that already has a promise. This avoids one of the common promise anti-patterns.
Subsequent calls to the API just chain to the original promise.
There is no need to use $.when() when you just have a single promise. You can just use .then() directly on it.
All errors automatically percolate back to the original promise.
This uses the ES6-standard .then() (which actually becomes more legitimately standard in jQuery 3.x - though it works in jQuery 1.x and 2.x with its own non-standard quirks) which makes this logic more compatible with other promise-producing async functions.
Also, a number of other retry ideas here: Promise Retry Design Patterns

Adding extra data to result of a promise

I'm using a node library which allows simple http requests to be made and returns a promise - this is working nicely, as it allows several to be made in parallel, and I'm then collecting them later with Promise.all(). However, the http request simply returns a string, and I need to know some extra identifying information about each request. So I though one way to do this was to chain something off the request promise and add that information in. This is the code I've got so far showing one such request being added to the array of promises I'll collect later:
var promises = [];
promises.push(new Promise(function(resolve, reject){
ReqPromise('http://some.domain.com/getresult.php')
.then(function(reqResult) {
resolve({
source: 'identifier',
value: reqResult
});
});
}));
And this is what I get back for this promise when it resolves:
{
source: 'identifier'
value: 1.2.3.4
}
Is this the ideal way to 'tag' a promise result? Or is there something about promises I'm misunderstanding which means I don't need to create an extra promise as above? Note that ReqPromise is in an external library and so it's hard to make it take extra parameters and return them.
Thanks to #Bergi for the link. This is the code I've ended up with, which I agree is much cleaner. I had tried something like this before, but must have made some mistake, as it was returning undefined to Promise.all(). But now is working nicely without extra promises.
promises.push(ReqPromise('http://some.domain.com/getresult.php').then(function(reqResult) {
return {
source: 'identifier',
value: reqResult
};
}));
#dsl101, going one step further than your own answer, it can be useful to .map() an array of data to an array of promises, then associate each data item with its corresponding asynchronously-derived result.
For example, you might start with an aray of URLs.
var data = ['http://path/0', 'http://path/1', 'http://path/2', ...];
var promises = data.map(function(url) {
return ReqPromise(url).then(function(reqResult) {
return {
'source': url,
'value': reqResult
};
});
});
Promise.all(promises).then(function(results) {
// each result has a .value and a corresponding .source (url) property
});
With async/await syntax introduced in ES8, it's possible to await a promise right inside an object literal
This is #dsl101's answer rewritten to use async/await
promises.push((async function() {
return {
source: 'identifier',
value: await ReqPromise('http://some.domain.com/getresult.php')
};
})());
And this is #Roamer-1888's
var data = ['http://path/0', 'http://path/1', 'http://path/2', ...];
var promises = data.map(async function(url) {
return {
'source': url,
'value': await ReqPromise(url)
};
});
var results = await Promise.all(promises)
functions can also be replaced with arrow functions
edit: note that in the pushing promises into array case, the function has to be called first to "become" a promise. in the .map case, the function is called by .map so it has already "become" a promise

Promise.all seems to resolve immediately

I posted a similar question related to Ember.RSVP.all, but I've found the behavior identical to how Promise.all behaves. Please let me know if I'm breaking any rules by submitting this separately.
I'm trying to use Promise.all in the middle of a chain of promises. The example I have is much simpler than my use, but it demonstrates the issue. In the middle of a chain of promises, I have a set of promises that all need to resolve before the chain can continue - exactly what I understand Promise.all to be for.
Unfortunately, when I return the Promise.all object, the next promise in the chain runs immediately, without waiting for the promises passed to all().
I've set up a js fiddle to demonstrate in the best way that I can think of:
Notice that First and Second both resolve at almost exactly the same time, when Second should be after the 1s promise comes back. Third and fourth follow as expected.
http://jsfiddle.net/vqut9zy2/
Fiddle code looks like this:
function delayAjax(delay) {
return $.ajax({
url: '/echo/json/',
data: {
json: '',
delay: delay,
}
});
}
delayAjax(1).then(function() {
$('#first').addClass('red');
var proms = [delayAjax(1), delayAjax(1)];
return Promise.all(proms).then(function() {
$('#onepointfive').addClass('red');
});
}).then(function() {
$('#second').addClass('red');
return delayAjax(1);
}).then(function() {
$('#third').addClass('red');
return delayAjax(1);
}).then(function() {
$('#fourth').addClass('red');
});
HTML
<div id="first">First</div>
<div id="onepointfive">One point five</div>
<div id="second">Second</div>
<div id="third">Third</div>
<div id="fourth">Fourth</div>
you need to convert jQuery's deferred to a promise first.
function delayAjax(delay) {
return Promise.resolve($.ajax({
url: '/echo/json/',
data: {
json: '',
delay: delay,
}
}));
}
http://jsfiddle.net/evilbuck/vqut9zy2/3/
There is a section in You Don't Know JS: Async & Performance that discusses how to manage untrustable thenables: Trustable Promise?.
It does indeed give Promise.resolve(thenable) as the way to approach this.
I don't know enough to explain why you were witnessing the behavior you were, but Domenic Denicola's article You're Missing the Point of Promises goes into some of the reasons why jQuery's deferreds are not genuine promises, and why they are problematic. At the end of the article, he provides the way to handle untrustable promises in the Q library, which is to use Q.when() (the Angularjs $q service also provides a .when() method for the same purpose):
return Q.when($.ajax({
url: '/echo/json/',
data: {
json: '',
delay: delay,
}
}));

Is there a better way of executing a bunch of partially async jQuery functions in a certain order?

I have four functions that must be done in a certain order. All but one of these functions do some asynchronous AJAX stuff in them, however they (necessarily) do other things too. So these are my own functions, which I've turned into deferred promise objects by doing this kind of thing:
function populateOfferSettings() {
return $.Deferred(function (deferred) {
//Whole bunch of stuff happens here, including async AJAX
deferred.resolve();
}).promise();
}
This actually works fine when combined with this:
populateOfferSettings().then(viewReady);
The problem arises when I have more than just two functions in play. In reality I have at least four. I tried the most intuitive solution that came to me and failed:
populateOfferSettings().then(populateSegmentationSettings).then(populateHousehold).then(viewReady);
For reasons I don't understand, all of those functions are executed but there's no waiting for the promises to complete — they just all fire off. So then I tried this, which actually works as expected and each function waits for the previous to resolve:
populateOfferSettings().then(function () {
populateSegmentationSettings().then(function () {
populateHousehold().then(function () {
viewReady();
});
});
});
This feels very verbose though, even if I write a helper function to simplify implementation. I also don't really understand why my initial attempt failed. Is there something I'm missing or is this as simple as I can get it?
You should be able to just keep calling then, but returning the next promise in the chain.
populateOfferSettings()
.then(function () { return populateSegmentationSettings(); })
.then(function () { return populateHousehold(); })
.then(function () { viewReady(); });
In jQuery 1.7.1 .pipe is always what you want. You might as well overwrite .then with .pipe since .then is absolutely useless before jQuery 1.8.
populateOfferSettings()
.pipe(populateSegmentationSettings)
.pipe(populateHousehold)
.pipe(viewReady);

javascript trouble with returning a promise- the returned promise is not executing

I have a function that looks like this
this.getToken = function() {
if (token === null) {
token = getAccessTokenAsync("username", "password");
lastTokenTime = getTokenExpiryAsync();
}
}
this function will call getAccessTokenAsync which will make a request to my web server with xhr. This looks like this:
getAccessTokenAsync = function (username, password) {
var serializedData = {
username: username, password: password,
};
return new WinJS.Promise(function (complete) {
WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).done(
function complete(result){
return JSON.parse(result.responseText);
}
);
})
}
I would expect token to now store a promise inside of it. Which when we then call .done() or .next() will have the json object which got returned by the server. However when I call getTokenExpiryAsync() something else happens.
getTokenExpiryAsync = function () {
if (token === null) {
return new Date();
}
token.then(
function complete(result){
console.log(result);
},
function onerror(error) {
console.log(error);
},
function onprogress(data) {
});
}
instead it doesn't seem to call any of the functions in the .then() it just skips right past it!. Strict mode is enabled so my token variable does have a promise inside of it. Otherwise it would of errored as it wouldn't be able to find the .done() method?
my question is why is this happerning and how can I get the expected behaviour that I want (token having a promise stored in it from getAccessTokenAsync which I can access in other methods).
In your code, it's unnecessary to create a new WinJS.Promise because WinJS.xhr().then will return the promise you want. To give the background, there are two ways to attach completed handlers to a promise: .then and .done. Both take the same arguments but differ in return value. .done returns undefined, as it's meant to be used at the very end of a promise chain.
.then, on the other hand, returns a promise that's fulfilled when the completed (or error handler) returns, and the fulfillment value is the value returned from the completed handler (or the error handler).
(By the way, I've written a bunch more on promises to clarify issues like this. A short version can be found All about promises (Windows Dev Blog); a more complete version can be found in Appendix A, "Demystifying Promises," of my free ebook, Programming Windows Store Apps in HTML, CSS, and JavaScript, Second Edition, which is in it's second preview right now.)
When writing any async function of your own, the best pattern to use when calling other existing async functions (like WinJS.xhr) is to return a promise from its .then. So in your case, you want getAccessTokenAsync to look like this:
getAccessTokenAsync = function (username, password) {
var serializedData = {
username: username, password: password,
};
return WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).then(
function complete(result){
return JSON.parse(result.responseText);
}
);
})
}
This will return a promise, which you assign to token, whose fulfillment value will be the result from JSON.parse(result.responseText).
Let me explain now why your original use of new WinJS.Promise is incorrect--it's a common misunderstanding that my other writings clarity. The function argument that you give to the constructor here itself receives three arguments. Each argument is another function, each of which I call a "dispatcher," and you get one for complete, error, and progress. The body of your code inside the promise much call these dispatchers upon the appropriate events.
Those dispatchers, in turn, then call the completed, error, and progress handlers for any functions subscribed through the promise's .then or .done. Calling these dispatchers, in other words, is the only way that you actually triggers calls to those handlers.
Now in your original code you never actually call any of these. You've kept it simple by having the WinJS.Promise constructor just pay attention to the completed dispatcher. However, when your WinJS.xhr call completed, you're not calling this dispatcher. Part of the confusion is that you have an argument called complete, and then name your completed handler for WinJS.xhr().done "complete" as well. If you set breakpoints on the last JSON.parse call, it should be getting hit, but the value your returning just gets swallowed because it's never passed to the complete dispatcher.
To correct this, you'd want your original code to look like this:
return new WinJS.Promise(function (completeDispatch) { //Name the dispatcher for clarity
WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).done(
function (result) { //Keep this anonymous for clarity
completeDispatch(JSON.parse(result.responseText));
}
);
})
This should work as well. However, it's still easiest to just return the promise from WinJS.xhr().then() as I originally noted, because you don't need another promise wrapper at all.
With either of these changes, you should now see a call to the completed handler within getTokenExpiryAsync.
Let's talk about other parts of your code now. First of all, token will always be set to a promise even if there's an error condition, so you'll never see a null case inside getTokenExpiryAsync. Secondly, if you use the new WinJS.Promise code as above, you'll never see error or progress cases, because you're never calling the errorDispatcher or progressDispatcher. This is another good reason to just use the return from WinJS.xhr().then() instead.
So you'll need to think through your error handling a little more closely here. What, exactly, are the cases where you want to call new Date() for an expiry? Do you do this when the xhr call fails, or when the response from a successful call returns empty?
One way to handle errors is to use the new WinJS.Promise variant above, with WinJS.xhr().done(), where you subscribe an error handler to .done. In that error handler you then determine whether you want to propagate the error, or whether you want to still fulfill the wrapper promise with a new Date, by calling completeDispather(new Date());. For other errors, you'd call the errorDispatcher. (Note that all this assumes that a successful xhr response contains the same format of data as new Date(), otherwise you're intermixing data values and would want to parse the date out of the response instead of just returning the whole response.)
return new WinJS.Promise(function (completeDispatch) { //Name the dispatcher for clarity
WinJS.xhr({
type: "post",
url: "http://127.0.0.1:8080/authenticate/login",
responseType: "json",
data: JSON.stringify(serializedData)
}).done(
function (result) { //Keep this anonymous for clarity
completeDispatch(JSON.parse(result.responseText));
},
function (e) {
completeDispatch(new Date()); //Turns an xhr error into success with a default.
}
);
})
What I've just described is really a good way to catch errors in your core operation and then inject a default value, which is what I believe you intend.
If you use the return value from WinJS.xhr().then(), on the other hand (the first code variant), then you need to put more of this logic inside getTokenExpiryAsync. (By the way, this code, as you show it, is synchronous, and one code path returns a new Date and the other return undefined, so it's not quite what you want.)
Now because token itself is a promise, this getTokenExpiryAsync does need to be async itself, and therefore needs to also return a promise for the expiry. Here's how you'd write that:
function getTokenExpiryAsync (token) { //I'd pass token as an argument here
return token.then(
function complete(result) {
return result; //Or parse the date from the original response.
},
function error(e) {
return new Date();
}
);
}
And then in your calling code you'll need to say:
getTokenExpiryAsync(token).then(function (expiry) {
lastTokenTime = expiry;
}
Again we're making use of the return value of then being another promise whose fulfillment value is what's returned from the completed or error methods. If token is in an error state (WinJS.xhr failed), then your call to .then will invoke the error handler, where you then return the desired default. Otherwise you return whatever expiry you want from the response. Either way, you get a date from this promise from .then in the original calling code.
I know this can be a bit confusing, but it's the nature of the Promises/A specification and async coding and not WinJS in particular.
Hope all this is worth your bounty. :)
It looks like your then functions aren't called because you aren't calling your promise function's complete callback. Also, your WinJS.xhr is a promise so you can just return that without wrapping it in another promise.

Categories

Resources