0

I've got a page that's doing a series of file uploads via AJAX post. Having the posts run as async=true was causing (maybe) communication) problems, so I converted them to async=false. That sorted the problems, but not some screen variables aren't updating as the files upload.

I think it's because now that the posts are not async, processing never leaves the function, so the changed observables never get a chance to be updated to the outside world (I'm surely describing that terribly)

I've added in "valueHasMutated" calls, but to no avail.

Values on screen "uploadBegins" and "manifestCount" are the variables in question.:

<!-- ko if: uploadBegins() --> <span data-bind="text:
 manifestCount()"></span> of <span data-bind="text: koXLSdata().length
 - (hasHeaderRow() ? 1 : 0)"></span>&nbsp;records complete 
<!-- /ko -->

uploadBegins should be set to true so the section becomes visible, and manifestCount should iterate on each upload.

Javascript (edited - hopefully I didn't leave something important out) (lines that reference variables pointed out with ---> ) :

    processUploads = function (callback) {
        callbacks.loading();
        getTabDataForModel(viewModel.selectedSupplier());

        var koXLSdata = viewModel.koXLSdata();
        viewModel.badUploads(0);
        viewModel.manifestCount(0);
--->    viewModel.uploadBegins(true);
--->    viewModel.uploadBegins.valueHasMutated();
        viewModel.uploadComplete(false);
        var filetoupload = null;
        var thisWasABadRec = false;
        var theCatCode = 0;

        for (var i = 0; i < viewModel.koXLSdata().length; i = i + 1) {
           uploadFile(koXLSdatarow, filetoupload, theCatCode, viewModel.pmiChecked(), i);
        }
  }

    uploadFile = function (fileData, file, theCatCode, pmiChecked, manifestRow) {
    //("D" contains file upload data, build not included for brevity)
        //vmfile.isUploading(true);
        $.ajax({
            async: false,
            type: "POST",
            url: apiPath + "AccountDocuments",
            data: d,
            cache: false,
            contentType: false,
            processData: false,
            success: function (jqXHR) {
                uploadFileSuccess(jqXHR, vmFile);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                uploadFileError(jqXHR, textStatus, errorThrown, vmFile)
            },
        });
    }

    uploadFileError = function (jqXHR, textStatus, errorThrown, file) {

        viewModel.badUploads(viewModel.badUploads() + 1);
--->    viewModel.manifestCount(viewModel.manifestCount() + 1);
--->    viewModel.manifestCount.valueHasMutated();
        checkForComplete(viewModel.manifestCount());
    },

    uploadFileSuccess = function (jqXHR, file) {

--->    viewModel.manifestCount(viewModel.manifestCount() + 1);
--->    viewModel.manifestCount.valueHasMutated();
        checkForComplete(viewModel.manifestCount());
    },

    checkForComplete = function (manifestCount) {
        var rowsOnly = 0;
        if (viewModel.hasHeaderRow)
            rowsOnly = viewModel.koXLSdata().length - 1;
        else
            rowsOnly = viewModel.koXLSdata().length;

        if (manifestCount === rowsOnly) {
            uploadFileComplete();
        }
    },

Again, the uploads are working, the issue is the screen updates. I'm not seeing the section open, nor the count rising. It pops up when the processing is done, with the proper count, like so:

enter image description here

valueHasMutated looked like it would force a change to the observable, but I suspect that the screen can't update while I'm in the loop...

...unless I do...a thing?

And I don't know what the "thing" is.

EDIT - "notifySubscribers()" didn't seem to do it either.

VBartilucci
  • 477
  • 6
  • 17
  • 1
    *"so I converted them to async=false"* - Don't ever run Ajax requests as synchronous. This is never the right solution. – Tomalak Sep 07 '18 at 19:44
  • The behavior you see is the direct result of synchronous processing. Your UI thread freezes as it waits for the requests to finish. No code whatsoever runs while a synchronous Ajax request is pending. You cannot even scroll the page. Get rid of the synchronous requests. – Tomalak Sep 07 '18 at 19:48
  • But alas, the async requests were dying on the vine for reasons as yet undiscovered. If I call uploadFile(as above) for each separate upload, the UI remains locked until...when? till after processUploads finishes its loop? Even though (as I stepped through it) each call was completing and passing down to uploadFileSuccess? Is there no way to get the ajax call to "end" so things will update? I assumed that by making it non-async, it would be MORE compartmentalized - it wouldn't be hanging about waiting for the response. – VBartilucci Sep 07 '18 at 19:55
  • Javascript is single-threaded. If you block the main thread (the UI thread) in any way - be it by showing a message box, or by doing a complex calculation, or by doing a synchronous Ajax request, then the world will literally grind to a stop until that thing you do is finished. And if you upload 50 files, nothing will happen until all of them are done. That's why one never does synchronous Ajax. Asynchronous Ajax is done on a background thread, so the UI thread can run unaffected. – Tomalak Sep 07 '18 at 20:04
  • 1
    So your main concern should be to discover why your asynchronous requests are dying. Maybe you need to employ some sort of throttling because the server does not tolerate too many concurrent uploads from one IP. – Tomalak Sep 07 '18 at 20:15
  • So many conflicting pages on how to add that - got a suggestion for one closest to the code I've already got? – VBartilucci Sep 07 '18 at 20:29
  • Hard to say. I don't even know if "too many uploads" is even the case here, it was only a guess that you should check on. In any case, it's likely not going to be fixed with a minimal rewrite. – Tomalak Sep 07 '18 at 20:34
  • (I haven't touched in knockout or frontend dev in a while but...) My two cents, in case you're not throttling your requests, I think the browser itself [will still do it nowadays](https://stackoverflow.com/questions/985431/max-parallel-http-connections-in-a-browser), which I'm not sure whether or not they could "timeout" even before being sent to the server (but anyways, 50 concurrent uploads are too many). – Pedro Sep 08 '18 at 12:47
  • One other thing, could you try writing your templates like this "ko if: uploadBegins" instead of this "ko if: uploadBegins()"? I think the parenthesis use the observable's value during the first rendering but doesn't subscribe to it. Without the parenthesis, it will use it on the first render and subscribe to future changes. You can also see and fiddle with one of the [official examples](http://jsfiddle.net/LkqTU/61175/). – Pedro Sep 08 '18 at 12:50

0 Answers0