Often I find myself writing beforeSave and afterSave using promises in this form:
beforeSavePromise: function (request) {
var recipe = request.object;
var promises = [
doFooIfNeeded(recipe),
doBarIfNeeded(recipe),
doQuuxIfNeeded(recipe)
];
return Parse.Promise.when(promises)
},
Each of these are conditional actions that perform an action only if a particular field or fields are dirty. So for example, doFooIfNeeded might look something like:
if (recipe.dirty('imageFile')) {
return /* some Promise that updates thumbnails of this image */;
} else {
return Parse.Promise.as(); // The no-op Promise. Do nothing!
}
My question is, is Parse.Promise.as() really the no-op Promise? or is new Parse.Promise() more correct?
With all "dirty" outcomes contributing a resolved promise to the aggregation, you can choose for each "clean" outcome to contribute in any of the following ways :
not to put anything in the array,
put a value in the array,
put a resolved promise in the array,
put a rejected promise in the array.
(1), (2) and (3) will guarantee that the aggregated promise resolves regardless of the clean/dirty outomes (except some unpredicted error).
(4) will cause the aggregared promise to resolve only if all outcomes are "dirty", or to reject as soon as any one "clean" outcome arises.
Realistically, the choice is between (2) and (4), depending on how you want the aggregated promise to behave. (1) would complicate the aggregation process, and (3) would be unnecessarily expensive.
It would seem appropriate for the aggregated promise to resolve when everything is either already "clean" or has been cleaned up, therefore I would suggest (2), in which case your foo()/bar()/quux() functions could be written as follows :
function foo() {
return recipe.dirty('imageFile') ? updates_thumbnails() : true; // doesn't have to be `true` or even truthy - could be almost anything except a rejected promise.
}
And aggregate the outcomes as in the question :
$.when([ foo(recipe), bar(recipe), quux(recipe) ]).then(function() {
//all thumbnails were successfully updated.
}).fail(function() {
//an unexpected error occurred in foo(), bar() or quux().
});
Parse.Promise.as() will technically give you a Promise with its state set to resolved. When you return this Promise, its callback will be triggered successfully. You can supply a value as an argument which basically triggers the callback with that value. According to the Parse guide on Promise creation, new Parse.Promise() creates a Promise that its states is neither set to resolved nor failed. This gives you the flexibility to mange its state manually as you wish.
Related
I have some code like this in a test:
console.log(workingStore$.getValue());
await lastValueFrom(workingStore$);
The console.log returns a value, yet my lastValueFrom call makes an error of EmptyError: no elements in sequence. Not sure how it's possible to receive values but be told I have no elements.
This test failed cause some order of operations changed. But basically I needed to do this:
const promise = lastValueFrom(workingStore$);
// logic that populates workingStore$
await promise;
Then things worked.
I was working on a react project and the following three cases came up. Can someone give me some pointers as to what the differences are when making AJAX request using axios as well as redux-promise?
Case 1 (the payload is undefined - why?):
axios.get(link)
.then(callback)
Case 2 (the payload is also undefined - why?):
axios.get(link)
.then(() => callback())
Case 3 (the payload is the res object):
axios.get(link)
.then((res) => { callback; return res });
Would really appreciate if anyone could please answer this question or give me pointers to materials that could clear my confusion. I did my best researching and have spent couple hours before I posted the question on SO. Thanks!
Your resolved value in the first two promises is undefined because you have a .then() handler that you don't return anything from. The return value of the .then() handler becomes the resolved value of the promise chain.
If you want link to be the resolved value of the promise and you're going to have your own .then() handler, then you have to return it.
Case 1:
axios.get(link).then(callback)
Here you're delegating your .then() handler to the callback function. Unless it returns the value it was passed to it (which apparently it doesn't), then the return value from your .then() handler is undefined and thus that's what the resolved value of the promise chain becomes.
Case 1 is equivalent to this:
axios.get(link).then(function(val) {
return callback(val);
});
So, the promise chain takes on whatever callback(val) returns as the resolved value.
Case 2:
axios.get(link).then(() => callback())
Here, we can clearly see that the return value from the .then() arrow function handler is the result of executing callback() which is apparently undefined so thus the promise chain takes on a resolved value of undefined.
You could fix that by doing this:
axios.get(link).then(() => {
callback()
return link;
});
Case 3:
axios.get(link).then((res) => { callback; return res });
Here's you're explicitly returning res so that becomes the resolved value of the promise chain. You're also not even calling callback(). I presume you meant to have () after it.
Don't mix plain callbacks in with promises
You also probably don't want to mix plain callbacks with promises and they are two different approaches to asynchronous notification and it's much better to use only one technique in a given section of code. Instead, turn the callback operation into one that returns a promise that is resolved when the callback would normally get called. Then, you can just chain promises.
Or, if you're trying to notify some caller about the completion of the operation, then have them just watch the returned promise with their own .then() handler on the returned promise.
This question already has answers here:
Proper way to skip a then function in Q Promises
(4 answers)
Closed 6 years ago.
Using When.js, we have a situation where we want to quietly abort a promise chain mid way, due to the user changing their mind. Our current method is to simply never resolve that step of the chain - effectively leaving the other promises "hanging". This seems slightly dirty?
If we reject the promise, then of course our exception handlers kick in. We could work around that, using a custom message which we detect and ignore, but that also seems a bit unclean.
Is there a better approach?
This is what the code looks like:
return getConfirmation(confirmConversion, 'Ready to upload your file to the ' + terria.appName + ' conversion service?')
.then(function() {
return loadItem(createCatalogMemberFromType('ogr', terria), name, fileOrUrl);
});
function getConfirmation(confirmConversion, message) {
...
var d = when.defer(); // there's no `when.promise(resolver)` in when 1.7.1
PopupMessageConfirmationViewModel.open('ui', {
...
confirmAction: d.resolve,
denyAction: function() { this.close(); /* Do nothing or d.reject(); ? */ }
});
return d.promise;
}
Result
For completeness, I changed the code to:
confirmAction: function () { d.resolve(true); },
enableDeny: true,
denyAction: function() { this.close(); d.resolve(false); }
and
.then(function(confirmed) {
return confirmed ? loadItem(createCatalogMemberFromType('ogr', terria), name, fileOrUrl) : undefined;
});
Making my comment into an answer:
If you're trying to return three possible states (resolve, rejected and user cancelled) so your code can handle all three possible resolutions correctly and you're using promises, then you will have to make either the resolved value indicate that the user cancelled or the reject reason will have to indicate cancel and your code will have to check for that.
There are only two possible final states for a promise, not three so you'll have to communicate the third state in one of the other two.
I'd recommend not stranding promises in the pending state unless you're absolutely sure they won't lead to a memory leak, but even then it doesn't seem like a very clean design to just strand them.
I am wondering if there's a way to create a promise chain that I can build based on a series of if statements and somehow trigger it at the end. For example:
// Get response from some call
callback = (response) {
var chain = Q(response.userData)
if (!response.connected) {
chain = chain.then(connectUser)
}
if (!response.exists) {
chain = chain.then(addUser)
}
// etc...
// Finally somehow trigger the chain
chain.trigger().then(successCallback, failCallback)
}
A promise represents an operation that has already started. You can't trigger() a promise chain, since the promise chain is already running.
While you can get around this by creating a deferred and then queuing around it and eventually resolving it later - this is not optimal. If you drop the .trigger from the last line though, I suspect your task will work as expected - the only difference is that it will queue the operations and start them rather than wait:
var q = Q();
if(false){
q = q.then(function(el){ return Q.delay(1000,"Hello");
} else {
q = q.then(function(el){ return Q.delay(1000,"Hi");
}
q.then(function(res){
console.log(res); // logs "Hi"
});
The key points here are:
A promise represents an already started operation.
You can append .then handlers to a promise even after it resolved and it will still execute predictably.
Good luck, and happy coding
As Benjamin says ...
... but you might also like to consider something slightly different. Try turning the code inside-out; build the then chain unconditionally and perform the tests inside the .then() callbacks.
function foo(response) {
return = Q().then(function() {
return (response.connected) ? null : connectUser(response.userData);
}).then(function() {
return (response.exists) ? null : addUser(response.userData);//assuming addUser() accepts response.userData
});
}
I think you will get away with returning nulls - if null doesn't work, then try Q() (in two places).
If my assumption about what is passed to addUser() is correct, then you don't need to worry about passing data down the chain - response remains available in the closure formed by the outer function. If this assumption is incorrect, then no worries - simply arrange for connectUser to return whatever is necessary and pick it up in the second .then.
I would regard this approach to be more elegant than conditional chain building, even though it is less efficient. That said, you are unlikely ever to notice the difference.
Why can't I set a (custom user-defined) property of a Deferred object to a promise returned by that same Deferred object? I think it is possible because dojo.store.JsonRest does it:
var results = xhr("GET", {
url: this.target + (query || ""),
handleAs: "json",
headers: headers
});
results.total = results.then(function(){
var range = results.ioArgs.xhr.getResponseHeader("Content-Range");
return range && (range=range.match(/\/(.*)/)) && +range[1];
});
return QueryResults(results);
I have to do something very similar, but I chain on an extra deferred object because I need to transform my JSON input to the format QueryResults() expects. If I assign that promise to a property of my Deferred object, it results in undefined. However, if I assign the promise to a regular variable the assignment works. Unfortunately, QueryResults needs the property to be assigned.
I think I have boiled down the problem to a difference between the promises returned by xhr() and xhr().then(). Please check the console output of this jsFiddle.
What is the difference, and how can I work around it?
The straightforward reason why you can't change the property is that the promise object is frozen:
that is, prevents new properties from being added to it; prevents existing properties from being removed; and prevents existing properties, or their enumerability, configurability, or writability, from being changed. In essence the object is made effectively immutable.
Dojo documentation explains why the promise is frozen:
The .then() call returns a new promise that represents the result of the execution of the callback. The callbacks will never affect the original promise's value.
The reason why you can change the property of Deferred returned by xhr() is that
The dojo.Deferred is a type of promise that provides methods for fulfilling the promise with a successful result or an error.
Actually, the promise of Deferred is stored is Deferred's property Deferred.promise, which is also frozen and Deferred.then() just links to Deferred.promise.then().
I would not recommend it, but if you insist on changing the promise you can achieve it via lang.delegate():
my_deferred = lang.delegate(my_deferred);
I modified your jsFiddle a bit to clarify the aforementioned: http://jsfiddle.net/phusick/N3J7M/.