1

I have an uploadList object populated by the code in block 2, but when I step through this code I will intermittently see the return uploadList line reached before the issueRepository.GetIssuesOffline function. This means that when I later loop through the uploadList (as per block 1), the r.amount, r.returns, and r.barcode are NULL, as they have not been set yet (the u.issue is undefined).

If I put a setTimeout on the call to Block 1 the code seems to execute all the way through if I give it long enough to process, but without a SetTimeout the uploadList looks almost like it is returning asynchronously before waiting for all the previous functions to complete.

Can anyone see what I am missing here, and understand why the issue object would not be defined in the processIssues function before block 2 completes and returns?

Block 1:

    var returns = Enumerable
        .From(uploadList)
        .Select(function (u) {
            var i = u.issue();
            var r = i.Returns();

            return {
                amount: r.amount,
                Totes: r.totes,
                barcode: r.barcode,
                Depo: i.supplier == null ? 'Unknown supplier' : i.supplier.name,
                StatusID: i.Status.Code,
                SupplierID: i.supplier.id,
                DepoFullName: i.depot
            }
        })
        .ToArray();

Block 2:

me.getIssuesFromReturnsList = function () {
    var uploadList = [];
    $.each(me.returnsList(), function (index, i) {
        var issue = issueRepository.GetDefaultIssue();
        issue.productname = "Loading from server...";
        issue.barcode = i.barcode;
        issue.ReturnsAmount(i.amount);

        var uploadItem = {
            barcode: i.barcode,
            amount: i.amount,
            issue: ko.observable(issue)
        };

        uploadList.push(uploadItem);

        issueRepository.GetIssuesOffline(i.barcode, function (issues) {
            if (issues.length > 0) {
                uploadItem.issue(issues[0]);
            }
        });
    });

    return uploadList;
}

Code behind some of these functions for reference is as follows:

GetIssuesOffline:

    GetIssuesOffline: function (barcode, callback) {
        var me = this;

        issueDatabase.GetIssues(barcode, function (issues) {
            me._processIssues(issues);

            return callback(issues);
        });
    }

issueDatabase.getIssues:

    GetIssues: function (barcode, callback) {
        var me = this;
        db.transaction(
            function (context) {
                var query = "SELECT * FROM issues WHERE barcode LIKE '" + barcode + "%' ORDER BY offSaleDate DESC LIMIT 25";
                context.executeSql(query, [], function (context, result) {
                    var issues = [];

                    for (var i = 0; i < result.rows.length; i++) {
                        var issue = result.rows.item(i);
                        issue.Source = dataSources.Local;
                        issue.isRanged = issue.isRanged == 1 ? true : false;
                        issues.push(issue);
                    }

                    callback(issues);
                }, me.ErrorHandler);
            }
            , me.ErrorHandler);
    }

me.processIssues:

    _processIssues: function (issues) {
        var me = this;
        $.each(issues, function (index, i) {
            if (i.issueNumber == null) {
                i.issueNumber = '';
            }

            i.issueNumber = i.issueNumber + '';
            i.productNumber = i.productNumber + '';

            if (i.issueNumber.length == 1) {
                i.issueNumber = '0' + i.issueNumber;
            }

            i.barcode = parseInt(i.productNumber + '' + i.issueNumber);
            i.Status = me.GetIssueStatus(i);
            i.supplier = me.GetissueSupplierDetails(i);
            i.ReturnsAmount = ko.observable(0);
            i.Returns = ko.observable({ totes: [] });

                returnsRepository.GetReturn(i.barcode, function (r) {
                    i.ReturnsAmount(r.amount);

                    if (r.amount > 0) {
                        i.Returns(r);
                    } else {
                        i.Returns({ totes: [] });
                    }
                });
            };
            i.RefreshReturnsAmount();

            me.IssueDatabase.UpdateIssue(i, function (issue) {
                me.IssueDatabase.UpdateIssueLastUpdated(issue);
            });
        });
    }
RussAwesome
  • 464
  • 5
  • 18
  • 1
    That's quite a lot of code blocks to read through out of context - it would really help if you could reduce it to a [mcve] - for instance is it really necessary for `GetDefaultIssue` to return an object with that many properties to demonstrate the issue? – James Thorpe Nov 07 '17 at 08:58
  • Fair enough, James. I've removed that block as it only set some default values. The others I have left in as each function is calling another function, and I'm worried that one of them is asynchronous in some way which is causing the outer function to return early. – RussAwesome Nov 07 '17 at 09:03
  • 1
    Having read it through, I think you're suffering as per the above dupe - in this case, your "unmodified variable" are the `uploadItem`s contained in `uploadList`. – James Thorpe Nov 07 '17 at 09:07
  • Thanks James, after reading that link it looks like I need to somehow move the functions inside each other. I don't really understand enough about which bits are asynchronous yet, or what I could move where as I have inherited all this code and am trying to learn javascript and pick this apart. – RussAwesome Nov 07 '17 at 09:23
  • 1
    The problem I see is the scope of `uploadItem`. Variables in javascript are not scoped to the loop block but to the entire function so when your asynchronous `GetIssuesOffline` returns it's trying to update that variable, and it could be referencing the uploadItem object of any random index of the loop at that point. Most likely it will always be referencing the last object in the array by the time the call returns. – Jason Spake Nov 07 '17 at 18:58
  • Oh, Jason! That's really useful (if disheartening) to know, thank you. I'm thinking at this point that I bring every single nested function back out into one giant code block that runs one after the other to try and avoid the asynchronous issue and the scope of the variable in the outer block. Would that work (albeit possibly freezing the browser)? – RussAwesome Nov 08 '17 at 09:27

0 Answers0