Home > database >  Why is the status undefined from an AJAX call in the context.sync() Promise chain in office.js
Why is the status undefined from an AJAX call in the context.sync() Promise chain in office.js

Time:01-05

I am trying to build an Excel Add-In that uses AJAX to get data from another server depending on the content. I have created the following simple prototype, that makes a simple get call and writes the result in the cell. The AJAX call does not yet use the content of the Excel but that will be needed in the future.

Excel.run(function (context) {
    let sourceRange = context.workbook.getSelectedRange().load("values, rowCount, columnCount");
    return context.sync()
        .then(function() {
            return $.ajax("https://localhost:44381/");
        })
        .then(function (data, status) {
            let values = [[ "Result:"   data   status ]];
            sourceRange.values = values;
        })
        .then(context.sync);
}).catch(errorHandler);

Everything seems to work except for the status, which is always undefined. It is very weird because the data is correct. I have tried it outside of the context.sync() promise chain, where it works perfectly fine. The status should be "success" and it does work with the then(function(data, status)... in a promise chain like this one. I have tried it with the following code:

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
  $("button").click(function(){
      

    $.ajax("https://localhost:44381/")
    .then(function(data, status){
      alert(data   status);
    })
    .then(function() {
      return $.ajax("https://localhost:44381/"); 
    })
    .then(function(data, status){
      alert(data   status);
    });
  });
});
</script>
</head>
<body>

<button>Send an HTTP GET request to a page and get the result back</button>

</body>
</html>

Why does it not receive the status? And how can I fix it?

CodePudding user response:

Standard promises only have a single "return value". There will never be a second argument to their .then() callbacks. context.sync() returns a standard promise, that's why status is not filled in your code.

jQuery's deferred implementation on the other hand does things differently, and jQuery's own .then() callback can receive more than one argument. $.ajax() returns a jQuery deferred.

Deferreds are largely compatible to standard promises, that's why you can combine them in the first place. But deviations from the standard, like secondary callback arguments, are lost.

If you need to capture the status argument into a regular promise, you must use jQuery's own .then() as a helper:

return context.sync()
  .then(() => {
    return $.ajax("https://localhost:44381/").then((data, status) => {
      return {data, status};
    });
  }).then(response => {
    // now you have response.data and response.status
  });

Here jQuery's .then() produces a single value by wrapping the individual arguments in an object {data: ..., status: ...}, and passes it on to the enclosing native promise.


You can make a wrapper around $.ajax() if you need it more often.

function jqAjax() {
  return new Promise((resolve, reject) =>
    $.ajax.apply($, arguments).then(
      (data, status, jqXHR) => resolve({data, status, jqXHR}),
      (jqXHR, status, error) => reject({error, status, jqXHR})
    )
  );
}

This will give you standard promise behavior, in exchange you will lose access to jQuery's own deferred API, i.e. things like .done() or .always().

  •  Tags:  
  • Related