1

Similar question to Promise resolve before inner promise resolved but I can't get it to work nontheless.

Every time I think I understand promises, I prove myself wrong!

I have functions that are written like this

function getFileBinaryData () {
    var promise = new RSVP.Promise(function(resolve, reject){
        var executorBody = {
            url: rootSite + sourceRelativeUrl + "/_api/web/GetFileByServerRelativeUrl('" + fileUrl + "')/$value",
            method: "GET",
            binaryStringResponseBody: true,
            success: function (fileData) {
                resolve(fileData.body);
            },
            error: function  (argument) {

                alert("could not get file binary body")
            }            
        }
        sourceExecutor.executeAsync(executorBody);
    });
    return promise;
}

function copyFileAction (fileBinaryData) {
    var promise = new RSVP.Promise(function(resolve, reject){
        var executorBody = {
            url: rootSite + targetWebRelativeUrl + "/_api/web/GetFolderByServerRelativeUrl('" + targetList + "')/Files/Add(url='" + fileName + "." + fileExt + "', overwrite=true)",
            method: "POST",
            headers: {
                "Accept": "application/json; odata=verbose"
            },
            contentType: "application/json;odata=verbose",
            binaryStringRequestBody: true,
            body: fileBinaryData,
            success: function (copyFileData) {
                resolve();
            },
            error: function  (sender, args) {

            }
        }
        targetExecutor.executeAsync(executorBody);    
    });
    return promise;
}

that I try to chain like this

$.getScript("/_layouts/15/SP.RequestExecutor.js")
            .then(patchRequestExecutor)
            .then(function(){
                sourceExecutor = new SP.RequestExecutor(sourceFullUrl);
                targetExecutor = new SP.RequestExecutor(targetList);
            })
            .then(getFileInformation)
            .then(getFileBinaryData)
            .then(copyFileAction)
            .then(getTargetListItem)
            .then(updateTargetListItem)
            .catch(function (sender, args) {

            });

or like this

$.getScript("/_layouts/15/SP.RequestExecutor.js")
            .then(patchRequestExecutor)
            .then(function(){
                sourceExecutor = new SP.RequestExecutor(sourceFullUrl);
                targetExecutor = new SP.RequestExecutor(targetList);
            })
            .then(function(){
                return getFileInformation();
            })
            .then(function(){
                return getFileBinaryData();
            })
            .then(function(binaryData){
                return copyFileAction(binaryData)
            })
            .then(function(){
                return getTargetListItem();
            })
            .then(function(listItem){
                return updateTargetListItem(listItem);
            });

The problem though is that even if I return new promises, the execution continues down the chain before any of the inner promises are resolved. How come? Shouldn't it wait until the asynchronous request has succeeded and resolve() is called in the success callback?

Community
  • 1
  • 1
Daniel B
  • 8,770
  • 5
  • 43
  • 76
  • Could you please provide a smaller and complete snippet that reproduces the issue? From here I don't see any flaws in your code, should be working. – Tamas Hegedus Sep 09 '15 at 09:48
  • It must be the fact that not all async functions here returns a promise. I'll try to modify the code to make it happen! – Daniel B Sep 09 '15 at 10:08
  • 1
    Don't forget to `reject` in the error callbacks. You really should replace that `alert` for examplel – Bergi Sep 09 '15 at 13:36
  • Absolutely! It was more of a debug assistant to see where it might've failed! – Daniel B Sep 09 '15 at 13:37

1 Answers1

3

You've done nothing wrong here. This is jQuery's fault, as so often1.

The problem is that jQuery is not Promises/A+ compatible (until v 3.0), and fails to adopt promises/thenable from other implementations than its own. So when your callbacks do return RSVP promises, jQuery just treats them as values to fulfill with, instead of waiting for them.
You could cast all of your promises to a jQuery deferred and it would work, but you really don't want that. To get the standard Promise behaviour (as offered by RSVP) to work, you will need to avoid jQuery's buggy then. This can easily be done:

RSVP.Promise.resolve($.getScript("/_layouts/15/SP.RequestExecutor.js"))
//   ^^^^^^^^^^^^^^^
    .then(patchRequestExecutor)
    .then(function(){
        sourceExecutor = new SP.RequestExecutor(sourceFullUrl);
        targetExecutor = new SP.RequestExecutor(targetList);
    })
    .then(getFileInformation)
    .then(getFileBinaryData)
    .then(copyFileAction)
    …
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1): This also happened [here](http://stackoverflow.com/a/31976947/1048572) with native promises and [here](http://stackoverflow.com/a/31462113/1048572) with RSVP – Bergi Sep 09 '15 at 13:29
  • Geebus, I never thought it could be because of that, but it does work now! Many thanks! – Daniel B Sep 09 '15 at 13:31