Ajax - Value returning undefined, but works when using alert/log

I know this is a common asynchronous issue and I know it has been answered and I know I need to use a callback function, but I can’t figure out how to apply the answers to my code.

This function creates some HTML that is later outputted. In this function you’ll see I call the getAttribution function (see below for code)

function showPhotos(cC, pI, category, photo) {
	choice += '\t<div class="col-lg-6">';
	choice += '\t\t<div class="item">';
	choice += '\t\t\t<input id="question_photo_'+cC+'" type="radio" name="'+category+'" value="'+photo+'" class="required">';
	choice += '\t\t\t<label for="question_photo_'+cC+'"><img src="photos/'+photo+'_'+pI+'.jpg" alt="">'+getAttribution(photo+'_'+pI)+'</label>';
	choice += '\t\t</div>';
	choice += '\t</div>';
}

The getAttribution function queries the database then compares the “filename” column with the “filename” variable I passed on to the function. If these match it outputs the name of the associated photographer.

function getAttribution(filename) {
	request = $.ajax({url: "attributions.php", dataType: "json"})
	
	request.done(function(finder) {
		for (var i = 0; i < finder.length; i++) {
			if (finder[i]['filename'] == filename) {
				return finder[i]['photographer'];
				break;
			}
		}
	});		
}

The getAttribution function runs immediately and returns nothing.
Later on the ajax request occurs, and when when it gains a response the for loop happens.

I recommend that from the request.done function, that you use the for attribute to find the appropriate element and update the label from there.

To be a little more precise…

This is the first “A” in “AJAX”. Asynchronous.

Your function gets called. Invokes an AJAX request - and then immediately continues executing code while that request fires off to go get its data.

Because all of the code in that function is related to the AJAX request, there’s nothing else for the function to do, so it returns, passing its default, undefined, back.

At some point in the future, the AJAX request gets done, and fires its .done() function. Returning something in this function is meaningless - it doesn’t go anywhere.

(incidentally, the break; line would never get executed - once a function returns, it has returned, and stops processing code beyond that point.)

How do you change your workflow? Few ways possible off the top of my head:
1: Encase the entire outer function(s) inside the done/success clause of the ajax call.
2: Store the result of the outer function in a global variable/the DOM, and when the AJAX call finishes, have its done() function manipulate that variable.

Forget that. This would require knowledge of when the AJAX is done. It’s better to use the Promise that done() (although then() would be more efficient here) returns to attach another handler.

Well, inherently, the ajax method knows when it’s done. That’s the point of the done() clause in the first place. Attaching another handler would be redundant with done() (and i’m guessing done() is a wrapper for exactly this already).

Dont reinvent the wheel :wink:

Sometimes separation of concerns is a good enough reason (though not in this case).

That’s why I said that then() would make more sense, since that changes the value the next handler would get passed.

request
    .then(finder => finder.filter(item => item.filename === filename))
    .then(finder => finder.map(item => item.photographer))
    .then(function (data) {
        if (data.length === 0) throw 'photo not found'
        return data[0]
    })
    .then(function (person) {
        // do something with person
    })
    .catch(function (error) {
        // alert(error)
    })

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.