jQuery .done not returning as expected - javascript

I know this is a misunderstanding on my part and i'm trying to learn what i'm doing wrong and I could really use some help please.
I have an ajax request that is succeeding and returning data just fine. The Api returns an error code, then I try to return a response based on what code I was given, except that the function is only returning what the ajax call delivered.
function getData(){
var promise = $.ajax({
url: 'api.php',
type: 'POST',
dataType: 'json'
});
return promise.done(function(data){
console.log(data);
if(data.errorCode === 0){
return data;
} else {
return 'failed';
}
});
}
$(document).ready(function(){
$('.btn').click(function(){
var apiData = getData();
console.log(apiData);
});
});
So if the api returns an error code that is not 0 then the function getData should return a string of 'failed', except it returns the data element. I know that the code is not 0 because the console.log shows that the code is 999. What am I doing wrong? Why I can't I get it to return my string of "failed" ?

getData doesn't (and can't) return the data or "failed"; it returns a promise. You consume that promise much the way you did inside getData:
$(document).ready(function(){
$('.btn').click(function(){
getData().done(function(apiData) { // **
console.log(apiData); // **
}); // **
});
});
In that callback, apiData will be whatever your callback in getData returned, so it'll be the data object or the string "failed".
(I've used done there because it's what you used elsewhere, but normally I'd use then, the standard promise function.)
One of the key things to understand about promises is that then (and done) returns a new promise (technically, with real promises, a thenable) that will be settled based on what the callback does.
So consider (let's stick with jQuery's Deferred for now):
function doSomething() {
var d = $.Deferred();
setTimeout(function() {
// Resolve our promise with "a"
d.resolve("a");
}, 10);
return d.promise();
}
// Consume the promise and put it through a chain:
doSomething()
.then(function(result) {
// This callback happens to do synchronous processing
console.log("First callback got", result);
return result.toUpperCase();
})
.then(function(result) {
// This one does something async, and so it returns a promise
var cd = $.Deferred();
setTimeout(function() {
console.log("Second callback got", result);
cd.resolve(result + result);
}, 10);
return cd.promise();
})
.then(function(result) {
console.log("Third callback got", result);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
The output of that is
First callback got a
Second callback got A
Third callback got AA
Now, earlier I said then always returns a promise (thenable). How does it do that when my first callback is synchronous and returns a value directly, but my second one is async and returns a promise? The then function looks at the return value from the callback and, if it's "thenable" (something with then on it), returns it; if it's not thenable, it creates a new, resolved promise with the value as the resolution value.
Just for completeness, here's the example above with native JavaScript promises (requires browser support):
function doSomething() {
return new Promise(function(resolve) {
setTimeout(function() {
// Resolve our promise with "a"
resolve("a");
}, 10);
});
}
// Consume the promise and put it through a chain:
doSomething()
.then(function(result) {
// This callback happens to do synchronous processing
console.log("First callback got", result);
return result.toUpperCase();
})
.then(function(result) {
// This one does something async, and so it returns a promise
return new Promise(function(resolve) {
setTimeout(function() {
console.log("Second callback got", result);
resolve(result + result);
}, 10);
});
})
.then(function(result) {
console.log("Third callback got", result);
});
The output of that is
First callback got a
Second callback got A
Third callback got AA

According to http://api.jquery.com/jquery.ajax/ documentation, you can either chain .done, .fail, .always or .then.
$(document).ready(function() {
$('.btn').click(function() {
$.ajax({
url: 'api.php',
type: 'POST',
dataType: 'json'
})
.done(function(data) {
console.log(data);
if (data.errorCode === 0) {
console.log(data)
} else {
console.log(data)
}
});
});
});
This will work

Related

Why can I return a recursive run of promises?

I have the following code (reconstructed almost 1:1 to work in here):
First of all, understand that bridge_component_start_test is really a call to a constructor object where we generate the initial promise by return new Promise.. I tried to mimick this here. It's my factory for AJAX calls, so, each object creates a promise, which makes a call, which then resolves / rejects with the JSON that it received from the server. I've emulated that response in the resolves below as well.
function recursive_function(data) {
let local_data = $.extend({}, data);
return new Promise((resolve, reject) => {
//The offset gets
local_data.bridge_process_batch_offset++;
if (local_data.bridge_process_batch_offset == 3) {
resolve({
'data': {
'response_data': {
'done_batching': true
}
},
'success': true
})
} else {
resolve({
'data': {
'response_data': {
'done_batching': false,
'bridge_process_batch_offset': local_data.bridge_process_batch_offset
}
},
'success': true
})
}
//For now, no reject.
});
}
function do_the_main_thing(data) {
/**
* Make a local copy of the passed data.
*/
let request_data = $.extend({}, data);
let bridging_data = {
'just_installed_component_demo': request_data.just_installed_component_demo,
'just_installed_component_name': request_data.just_installed_component_name,
'just_installed_component_category': request_data.just_installed_component_category,
'bridge_process_batch_offset': 0
};
const recursively_do_things = () => recursive_function(bridging_data).then(response => {
if (response.data.response_data.done_batching) {
return (response);
} else if (response.data.success == false) {
return response;
} else {
console.log('Went through step ' + response.data.response_data.bridge_process_batch_offset + '!');
if ('bridge_process_batch_offset' in response.data.response_data) {
bridging_data.bridge_process_batch_offset = response.data.response_data.bridge_process_batch_offset;
}
return recursively_do_things ();
}
}).catch(error => {
return error;
});
return recursively_do_things();
}
do_the_main_thing({
'just_installed_component_demo': 'demo-2',
'just_installed_component_name': 'demo-2_post',
'just_installed_component_category': 'post',
'bridge_process_batch_offset': 0
}).then(response => {
console.log('...and now I am done!');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
But notice, in my do_the_main_thing I'm merely returning the response from that promise recursion. Yes, surely, I also return recursively_bridge_component_start but how does JS know to return a promise out of that recursive functionality?
A return is a return. It doesn't have to wait for the Promise to finish, yet it seems it does. In theory, my last line of return recursively_do_things should just instantly return before the Promises are done.
What am I missing here?
But it's quite straightforward. The return value of bridge_component_start_in_test(data) is the return value of recursively_bridge_component_start().
That function in turn returns bridge_component_start_test(data).then(/* do stuff */).catch(/* do other stuff */). That will always be a promise regardless of details of "do stuff" and "do other stuff".
And bridge_component_start_test explicitely returns a promise: return new Promise(//...
You are not returning merely response from recursively_do_things
If you look at the function, you are returning a part of the chain, your return response is just part of the promise chain.
So this part
const recursively_do_things= () => recursive_function(bridging_data).then(response => /* omitted */ );
is returning you a promise chain implying that it first waits for recursive_function to complete, and then does the inner response, after which you can chain other statements
so, since you are returning return recursively_do_things() from your do_the_main_thing(data), it is clear that a promise will be returned.
Update based on comment
Promises are promise aware themselves, which means that if you return a promise in a chain, the promise chain will wait for the returned promise to complete before continuing the next part of the chain.
The requirement for that is that you need to return it, if you just call a function that returns a promise, it will not wait for it.

jQuery Deferred/promise not working as expected

Ok, so I've read 1,000,000+ articles on jQuery deferreds and/or promises, and I'm still getting something wrong.
functionOne() {
var deferred = $.Deferred();
var request = $.ajax({
url: 'http://example.com/mypath/etc'
});
request.done(function(data) {
// TODO: I've got stuff here that takes a while.
deferred.resolve();
});
return deferred.promise();
}
functionTwo() {
// Something that depends on the others being finished.
}
$.when(
functionOne(),
anotherLongRunningFunctionWithAjax())
.then(
functionTwo()
);
I need any function(s) in the "when" to fully complete (.ajax done) before the "then" starts. However, the promise returns immediately (as expected), but functionTwo starts, even though functionOne has not called "done".
I'm sure it's a fundamental misunderstanding of deferred and the chain-of-calls.
Edit:
function functionOne() {
console.log('functionOne called');
var request = $.ajax({
url: 'http://example.com/mypath/etc'
});
request.done(function(data) {
console.log('Starting Done.');
setTimeout(function () {
console.log('Done finished.');
}, 5000);
});
console.log('Returning promise.');
return request;
}
function functionTwo() {
console.log('functionTwo called');
}
$.when(functionOne()).then(functionTwo());
Gives me this in the console:
functionOne called
Returning promise.
functionTwo called (should be called after Done is finished.)
Starting Done.
Done finished.
Taking the code in your edit, there are two issues:
The timer in functionOne starts after request is resolved, yet you return request. So whatever happens with the timer... it is of not relevance to the returned promise, which at that time is already resolved.
You call functionTwo immediately, instead of passing the function reference for the $.when promise to call back
Here is working code:
function functionOne() {
console.log('functionOne called');
console.log('Returning promise.');
return $.ajax({
url: 'https://jsonplaceholder.typicode.com/posts/1'
}).then(function(data) {
console.log('Starting Done.');
var dfd = $.Deferred();
setTimeout(function () {
console.log('Done finished.');
dfd.resolve(data); // indicate when you are done
}, 2000); // 2 seconds will do ;-)
return dfd.promise(); // you need to return a promise again
});
}
function functionTwo() {
console.log('functionTwo called');
}
// don't call functionTwo immediately, leave that to the promise to do:
$.when(functionOne()).then(functionTwo);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
you are using an anti-pattern since $.ajax itself returns a promise
Just do
functionOne() {
var request = $.ajax({
url: 'http://example.com/mypath/etc'
});
request.done(function(data) {
// TODO: I've got stuff here that takes a while.
});
return request
}

Return from a promise then()

I have got a javascript code like this:
function justTesting() {
promise.then(function(output) {
return output + 1;
});
}
var test = justTesting();
I have got always an undefined value for the var test. I think that it is because the promises are not resolved yet..there is a way to return a value from a promise?
When you return something from a then() callback, it's a bit magic. If you return a value, the next then() is called with that value. However, if you return something promise-like, the next then() waits on it, and is only called when that promise settles (succeeds/fails).
Source: https://web.dev/promises/#queuing-asynchronous-actions
To use a promise, you have to either call a function that creates a promise or you have to create one yourself. You don't really describe what problem you're really trying to solve, but here's how you would create a promise yourself:
function justTesting(input) {
return new Promise(function(resolve, reject) {
// some async operation here
setTimeout(function() {
// resolve the promise with some value
resolve(input + 10);
}, 500);
});
}
justTesting(29).then(function(val) {
// you access the value from the promise here
log(val);
});
// display output in snippet
function log(x) {
document.write(x);
}
Or, if you already have a function that returns a promise, you can use that function and return its promise:
// function that returns a promise
function delay(t) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, t);
});
}
function justTesting(input) {
return delay(100).then(function() {
return input + 10;
});
}
justTesting(29).then(function(val) {
// you access the value from the promise here
log(val);
});
// display output in snippet
function log(x) {
document.write(x);
}
What I have done here is that I have returned a promise from the justTesting function. You can then get the result when the function is resolved.
// new answer
function justTesting() {
return new Promise((resolve, reject) => {
if (true) {
return resolve("testing");
} else {
return reject("promise failed");
}
});
}
justTesting()
.then(res => {
let test = res;
// do something with the output :)
})
.catch(err => {
console.log(err);
});
Hope this helps!
// old answer
function justTesting() {
return promise.then(function(output) {
return output + 1;
});
}
justTesting().then((res) => {
var test = res;
// do something with the output :)
}
I prefer to use "await" command and async functions to get rid of confusions of promises,
In this case I would write an asynchronous function first,
this will be used instead of the anonymous function called under "promise.then" part of this question :
async function SubFunction(output){
// Call to database , returns a promise, like an Ajax call etc :
const response = await axios.get( GetApiHost() + '/api/some_endpoint')
// Return :
return response;
}
and then I would call this function from main function :
async function justTesting() {
const lv_result = await SubFunction(output);
return lv_result + 1;
}
Noting that I returned both main function and sub function to async functions here.
Promises don't "return" values, they pass them to a callback (which you supply with .then()).
It's probably trying to say that you're supposed to do resolve(someObject); inside the promise implementation.
Then in your then code you can reference someObject to do what you want.
I think what the original poster wants is to return an unwrapped value from a promise without actually returning another promise. Unless proven otherwise, I'm afraid this is not possible outside of a then() or async/await context. You always get a promise no matter what.
You need to make use of reference data type like array or object.
function foo(u,n){
let result = [];
const userBrands = new Promise((res, rej)=> {
res(['brand 1', 'brand 3']);
})
userBrands.then((ub)=>{
return new Promise((res, rej) =>{
res([...ub, 'brand 4', 'brand 5']);
})
}).then(response => {
return result.push(...response);
});
return result;
};
foo();
You cannot return value after resolving promise. Instead call another function when promise is resolved:
function justTesting() {
promise.then(function(output) {
// instead of return call another function
afterResolve(output + 1);
});
}
function afterResolve(result) {
// do something with result
}
var test = justTesting();

Javascript promise not delaying function execution

I'm using ES6 javascript promises in Chrome and am having trouble understanding why the promise executed within the function _getStatus() is not returning the result argument in the success handler which would result in the alert box containing "done". Instead, I get an alert box that says "undefined".
myNameSpace = function(){
var obj = {
groupA: {
status: "inactive"
},
groupB: {
status: "active"
}
};
function _getStatus(group){
_finishTask().then(function(result){
return result; // doesn't return anything
});
}
function _finishTask(){
var promise = new Promise(function(resolve, reject){
// do some task before resolving
resolve("done");
});
return promise;
};
return{
getStatus:_getStatus
}
}();
$(function(){
alert(myNameSpace.getStatus("groupA")); // why is this "undefined" instead of "done"?
});
Because this is not how Promises work. You need to make sure both _getStatus and _finishTask return Promise objects. Then you will be able to use those promises API methods to execute subsequent code what promise is resolved.
So your code should look something like this:
myNameSpace = function() {
var obj = {
groupA: {
status: "inactive"
},
groupB: {
status: "active"
}
};
function _getStatus(group) {
return _finishTask().then(function(result) {
return result + " and tested";
});
}
function _finishTask() {
return new Promise(function(resolve, reject) {
// do some task before resolving
resolve("done");
});
};
return {
getStatus: _getStatus
}
}();
myNameSpace.getStatus("groupA").then(alert);
Finally, regarding this construction:
return _finishTask().then(function(result) {
return result;
});
_finishTask returns a Promise object, when this promise is resolved you get into then callback. Whatever value you return from this callback becomes a new resolution value for the subsequent success callbacks down the resolution chain.
Demo: http://plnkr.co/edit/K1SWKuTYA3e46RxdzkCe?p=preview
You cant return a result from an asynchronous function as the code running it has already finished by the time the response is returned.
You can however pass in a callback to execute once the code is complete:
function _getStatus(group, callback){
_finishTask().then(function(result){
callback(result);
});
}
$(function(){
myNameSpace.getStatus("groupA", function(result) {
alert(result);
});
});
or use the promise api itself (in this case your _getStatus method is a little redundant):
function _getStatus(group){
return _finishTask();
}
$(function(){
myNameSpace.getStatus("groupA").then(function(result) {
alert(result);
});
});

How can they call def.then here?

I was reading about promises and found this fiddle created by the author of this post
The code is here:
var def, getData, updateUI, resolvePromise;
// The Promise and handler
def = new $.Deferred();
updateUI = function (data) {
$('p').html('I got the data!');
$('div').html(data);
};
getData = $.ajax({
url: '/echo/html/',
data: {
html: 'testhtml',
delay: 3
},
type: 'post'
})
.done(function(resp) {
return resp;
})
.fail(function (error) {
throw new Error("Error getting the data");
});
// Event Handler
resolvePromise = function (ev) {
ev.preventDefault();
def.resolve(ev.type, this);
return def.promise();
};
// Bind the Event
$(document).on('click', 'button', resolvePromise);
def.then(function() {
return getData;
})
.then(function(data) {
updateUI(data);
})
.done(function(promiseValue, el) {
console.log('The promise was resolved by: ', promiseValue, ' on ', el);
});
// Console output: The promise was resolved by: click on <button> </button>
I do understand from the first part of this series that a deferred has a promise which can be exposed using the promise method on it.
Promises have then method which returns a promise for chaining.
Here they resolve the promise on the deferred in resolvePromise,then the then method on the deferred which I dont think is a promise is executed.What am I missing here?
Deferred objects in jQuery are also thenables and you can use them in place of promises. Doing so is rather uncommon though.
var d = $.Deferred().resolve();
d.then(function(){
console.log("HI"); // this will run.
});
The original $.ajax having .done and .fail is pointless in this case, especially the .done whose return value is ignored and has no impact.
In all honestly, I think the code could be improved to something like rather easily:
var getData = $.post('/echo/html/', { html: 'testhtml', delay: 3 });
var d = $.Deferred();
$(document).on('click', 'button', function(ev){
d.resolve();
return false;
});
$.when(d, getData).then(function(_, data){
$('p').html('I got the data!');
$('div').html(data);
});
There is no point in .thening if you only use the identity function (that is, return the same thing and do nothing else.
There is no point in .doneing only to return the same thing.
Generally, I would advise against promises for handing events unless the events are strictly one time.

Categories

Resources