Adding Promises to the JavaScript XMLHttpRequest Object

Date Published: 4/15/2021

Check out my video courses at...

Pluralsight and at Udemy

Adding Promises to the JavaScript XMLHttpRequest Object

Asynchronous JavaScript And XML (Ajax) is the cornerstone of communication between your client-side and server-side code. Regardless of whether you use JavaScript, jQuery, Angular, React or any other client-side language, they all use Ajax under the hood to send and receive data from a web server. Using Ajax you can read data from, or send data to, a web server all without reloading the current web page. In other words, you can manipulate the DOM and the data for the web page without having to perform a post-back to the web server that hosts the web page. Ajax gives you a huge speed benefit because there is less data going back and forth across the internet. Once you learn how to interact with Ajax, you will find the concepts apply to whatever front-end language you use.

When using the XMLHttpRequest object you must use callbacks. Promises are a better mechanism for working with asynchronous calls today. In this article you create a wrapper around the XMLHttpRequest object so you can use a Promise instead of callbacks.

Download Starting Projects

Instead of creating a front-end web server and a .NET Core Web API server in this article I have two sample projects you may download to get started quickly. If you are unfamiliar with building a front-end web server and a .NET Core Web API server, you can build them from scratch step-by-step in my three blog posts listed below.

  1. Create CRUD Web API in .NET Core
  2. Create .NET Core MVC Application for Ajax Communication
  3. Create Node Web Server for Ajax Communication

You can find all three of these blog posts at https://www.pdsa.com/blog. Instructions for getting the samples that you can start with are contained in each blog post. You are going to need blog post #1, then choose the appropriate web server you wish to use for serving web pages; either .NET MVC (#2) or NodeJS (#3).

Start Both Projects

After you have reviewed the blog posts and downloaded the appropriate sample projects to your hard drive, start both projects running. The first project to load is the Web API project. Open the WebAPI folder in VS Code and click on the Run | Start Debugging menus to load and run the .NET Web API project.

Open the AjaxSample folder in VS Code.

If you are using node, open the AjaxSample folder in VS Code, open a Terminal window and type npm install. Then type npm run dev to start the web server running and to have it display the index page in your browser.

If you are using the .NET MVC application, open the AjaxSample-NET folder in VS Code and click on the Run | Start Debugging menus to load and run the .NET MVC project. The index.cshtml page should now be displayed in your browser window.

Try it Out

Go to your browser for the front-end web server (localhost:3000) and you should see a page that looks like Figure 1. If your browser looks like this, everything is working for your front-end web server.

Figure 1: This is the starting project from which you are going to build your CRUD logic using Ajax and .NET Core.

Open the Browser Tools in your browser, usually accomplished by clicking the F12 key. Click the Get Products button and you should see the product data retrieved from the Product table in the AdventureWorksLT database and displayed in your console window.

JavaScript and XMLHttpRequest using Promises

If you have old code using only JavaScript and you do not want to add jQuery to your application, you can still use promises. The Promise object is built-in to all modern browsers. You can build your own ajax wrapper function that uses the Promise object as shown in Listing 1.

The ajax wrapper function shown in Listing 1 is like the ajax wrapper function you wrote in the blog post "Using JavaScript and XMLHttpRequest to Call a .NET Core Web API". The big difference is the entire wrapper code is enclosed in the Promise object. The code shown in bold in Listing 1 highlights the differences between the ajax wrapper function from the earlier blog post and the new wrapper function that uses the Promise object. Open the ajax-common.js file and add the code shown in Listing 1 to this file.

function ajax(verb, url, data, fulfilled, rejected) {
  return new Promise(function (fulfilled, rejected) {
    // Create XMLHttpRequest object
    let req = new XMLHttpRequest();

    // When the entire request fails it is probably a network error
    req.onerror = function () {
      rejected(new Error('There was a network error.'));
    };

    // Setup state change event
    req.onreadystatechange = function () {
      if (this.readyState === XMLHttpRequest.DONE) {
        // Check status property to see what is going on
        if (this.status >= 200 && this.status < 400) {
          fulfilled(this);
        } else if (this.status >= 400) {
          rejected({
            "status": this.status,
            "statusText": this.statusText,
            "response": this.response,
            "responseText": this.responseText
          });
        }
      }
    };

    // Open Request
    req.open(verb, url);

    // Set headers for JSON
    req.setRequestHeader("Content-Type", "application/json");

    // Check to see if we need to pass data
    if (data) {
      // Submit the request with data
      req.send(JSON.stringify(data));
    }
    else {
      // Submit the request
      req.send();
    }
  });
}
Listing 1: You can use a Promise object in JavaScript to wrap up an Ajax call using the XMLHttpRequest object.

Get all Products

Open the index page and modify the get() function to look like the code shown in Listing 2. This function uses the ajax() function you just added to the ajax-common.js file. Use the .then() function if the Ajax call is successful and the Promise has been fulfilled. Use the .catch() function if there is a problem with the Ajax call and leaves the Promise in a rejected state.

function get() {
  ajax("GET", URL)
    .then(function (data) {
      displayMessage("Products Retrieved");
      console.log("Status: " + data.status);
      console.log(JSON.parse(data.response));
    })
    .catch(function (error) {
      handleAjaxError(error);
    });
}
Listing 2: You can simplify JavaScript GET calls using a promise.

Try it Out

Save all your changes and go back to the browser. Click on the Get All Products button and check the results in the console window.

Get a Single Product

Open the index page and modify the getProduct() function to look like the code shown in Listing 3. Pass in the product id retrieved from the input field and use the .then() function if the Ajax call is successful and the Promise has been fulfilled. Use the .catch() function if there is a problem with the Ajax call and leaves the Promise in a rejected state.

function getProduct() {
  // Gather data from input
  let product = getFromInput();

  ajax("GET", URL + "/" + product.productID)
    .then(function (data) {
      displayMessage("Product Retrieved");
      console.log("Status: " + data.status);
      setInput(JSON.parse(data.response));
    })
    .catch(function (error) {
      handleAjaxError(error);
    });
}
Listing 3: Get a single product using the ajax() function

Try it Out

Save all your changes and go back to the browser. Input a valid product id into the Product ID input field. Click on the Get a Product button and the data for the product should appear in the input fields.

Insert a Product

Modify the insertProduct() function to use the new ajax() function as shown in Listing 4.

function insertProduct() {
  // Gather data from input
  let product = getFromInput();

  ajax("POST", URL, product)
    .then(function (data) {
      displayMessage("Product Inserted");
      console.log("Status: " + data.status);
      setInput(JSON.parse(data.response));
    })
    .catch(function (error) {
      handleAjaxError(error);
    });
}
Listing 4: Modify the insertProduct() function to use a Promise.

Try it Out

Save all your changes and go back to the browser. Click on the Insert a Product button and you should see a new product id appear in the Product ID input field.

Update a Product

Modify the updateProduct() function to use the new ajax() function as shown in Listing 5.

function updateProduct() {
  // Gather data from input
  let product = getFromInput();

  ajax("PUT", URL + "/" + product.productID, product)
    .then(function (data) {
      displayMessage("Product Updated");
      console.log("Status: " + data.status);
      setInput(JSON.parse(data.response));
    })
    .catch(function (error) {
      handleAjaxError(error);
    });
}
Listing 5: Modify the updateProduct() function to use a Promise.

Try it Out

Save all your changes and go back to the browser. Click on the Update a Product button and you should see any updated product data in the input fields. Also, check the console window for the status returned from this call.

Delete a Product

Modify the deleteProduct() function to use the new ajax() function as shown in Listing 6.

function deleteProduct() {
  // Gather data from input
  let product = getFromInput();

  ajax("DELETE", URL + "/" + product.productID, null)
    .then(function (data) {
      displayMessage("Product Deleted");
      console.log("Status: " + data.status);
      console.log(data.response);
      clearInput();
    })
    .catch(function (error) {
      handleAjaxError(error);
    });
}
Listing 6: Modify the deleteProduct() function to use a Promise.

Try it Out

Save all your changes and go back to the browser. Click on the Update a Product button and you should see any updated product data in the input fields. Also, check the console window for the status returned from this call.

Summary

The Promise object in all modern browsers can let you implement your own Promise-based approach with any code you wish to write. It can even wrap up the Ajax calls using the XMLHttpRequest object.


#promises #xmlhttprequest #javascript #pauldsheriff #development #programming

Check out my video courses at...

Pluralsight and at Udemy