Search code examples
javascriptarraysasynchronouspromisersvp.js

RSVP.js - Multiple asynchronous function calls on array


I have a result from a REST call that contains a list of files. Each file has properties that I have to extract and place in a new array. This is straightforward and is easily done with a simple loop. Three of the properties I need to extract are accessible in a direct way, while three other properties are of HATEOAS type, which in other words mean that for each file in the result, I have to make three other asynchronous calls to retrieve it's values.

My first instinct was to use RSVP.all() to process my promises and to map the items in the new array to the corresponding properties in the original list of files using map, but I cannot figure out how to achieve this.

I want to achieve something like below, but I have no idea how I can get the index of the current mapped item in itemList, to include the correct file from fileList. How can I do this?

On a sidenote, if I use RSVP.all() the wrong way I'm happy to receive tips!

function createItemList(fileList) {
    var promise = new RSVP.Promise(function(resolve, reject) {
        var itemList = [];

        //For each file in fileList, get the directly accessible properties
        //and push it to a new array
        for (var i = 0, file; file = fileList[i]; i++) {
            currentItem.Name = file.Name;
            currentItem.LastModified = new Date(file.TimeLastModified).format("dd-MM-yyyy hh:mm:ss");
            currentItem.Version = file.MajorVersion + "." + file.MinorVersion;

            itemList.push(currentItem);
        }

        //This is where it starts to get messy...
        //I want to map each item in the new itemlist to the corresponding properties
        //in the fileList. If I can include the corresponding file somehow, I could set the
        //data in the method 'getModifiedBy' and similar. I believe this should work,
        //but I have no idea how I can achieve this.
        var modifiedBy = itemList.map(function(item) {
            return getModifiedBy(item, fileList[INDEX]);
        });

        var checkedOutBy = itemList.map(function (item) {
            return getCheckedOutBy(item, fileList[INDEX]);
        });

        var eventDate = itemList.map(function (item) {
            return getEventDate(item, fileList[INDEX]);
        });

        var promises = {
            promisesModifiedBy: modifiedBy,
            promisesCheckedOutBy: checkedOutBy,
            promisesEventDate: eventDate
        };

        RSVP.all(promises)
            .then(function() {
            resolve(itemList);
        });
    });
    return promise;
}

Solution

  • Use only a single map over the itemlist that returns a Promise for your 3-property-object. Use a dedicated helper function for single items.

    Btw, with new RSVP.Promise you're using the deferred antipattern while there's absolutely no reason - RSVP.all() already returns you the result promise.

    function createItemList(fileList) {
        return RSVP.all(fileList.map(createItem));
    }
    function createItem(file) {
        // get the directly accessible properties
        var currentItem = {}; 
        currentItem.Name = file.Name;
        currentItem.LastModified = new Date(file.TimeLastModified).format("dd-MM-yyyy hh:mm:ss");
        currentItem.Version = file.MajorVersion + "." + file.MinorVersion;
    
        return RSVP.hash({
            modifiedBy: getModifiedBy(currentItem, file),
            checkedOutBy: getCheckedOutBy(currentItem, file)
            eventDate: getEventDate(currentItem, file)
        }).then(function(asyncprops) {
            // somehow mix the asyncprops with the currentItem
            return item; // into a result item
        });
    }