Search code examples
javascriptjqueryajaxjquery-deferreddeferred

How to properly use jQuery Deferred inside of a loop


I'm trrying to call a function that loops through a dataset and updates values in that dataset from data that comes from an asynchronous function. I obviously need to know when that function completes the loop and updates.

This is what I have currently:

function loadStationData() {
    var $dfd = $.Deferred();
    $.getJSON('../backend/navigation/get-stations.php', function(data) {
        stationData = data;
        $dfd.resolve();
    });
    return $dfd.promise();
}

function updateStationData() {
    var $dfd = $.Deferred();
    $.each(stationData, (i,v) => {
        var $dfd2 = $.Deferred();
        $.when(getFullCoordPack(v.lat, v.lng)).then(function(coords) {
            delete v.lat;
            delete v.lng;
            v.coords = coords;
            $dfd2.resolve();
        });
        return $dfd2.promise();
    });
    return $dfd.promise();
}

$.when(loadStationData(), updateStationData()).then(() => {
    // do stuff
});

The function getFullCoordPack(v.lat, v.lng) is using What3Words's API and therefore contains and AJAX call, hence requiring a promise when it completed.

However, I'm struggling to understand how to use the Deferred object in the second function and require some advice on how best to achieve my goal. The first function works just fine.


Solution

  • $.getJSON already returns a promise, so there is no need to use a new Deferred for it.

    In the second function you don't have code that resolves $dfd.

    However, since ECMAScript 2015 there really is no good reason to still use jQuery Deferreds. Now promises are native in JavaScript, together with async / await syntax, and APIs that return promises, like fetch.

    So something like this should work (assuming that getFullCoordPack returns a promise object):

    async function getStations() {
        const response = await fetch('../backend/navigation/get-stations.php'); 
        const stationData = await response.json();
        return Promise.all(stationData.map(async ({lat, lng, ...rest}) => ({
            ...rest,
            coords: await getFullCoordPack(lat, lng)
        })));
    }
    
    getStations().then((stationData) => {
        // do stuff with stationData
    });