Usage of async await in Dave Gray’s react_fetch_api_data-main code

Note: This post is a details-post for the post: Learning web app development through free online tutorials – Organized Notes and Log, https://raviswdev.blogspot.com/2024/03/learning-web-app-development-through.html .

The related code is given below and later the usage is analyzed:

const apiRequest = async (url = '', optionsObj = null, errMsg = null) => {
    try {
        const response = await fetch(url, optionsObj);
        if (!response.ok) throw Error('Please reload the app');
    } catch (err) {
        errMsg = err.message;
    } finally {
        return errMsg;
    }
}
...
  const addItem = async (item) => {
    const id = items.length ? items[items.length - 1].id + 1 : 1;
    const myNewItem = { id, checked: false, item };
    const listItems = [...items, myNewItem];
    setItems(listItems);

    const postOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(myNewItem)
    }
    const result = await apiRequest(API_URL, postOptions);
    if (result) setFetchError(result);
  }
...
  const handleSubmit = (e) => {
    e.preventDefault();
    if (!newItem) return;
    addItem(newItem);
    setNewItem('');
  }
...
handleSubmit is NOT an async function. It calls the addItem async function directly without using an await. In what order does the code run after addItem() is called? To understand that, first some info. from async function, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function : “The body of an async function can be thought of as being split by zero or more await expressions. Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.”

Additional info from fetch() global function, https://developer.mozilla.org/en-US/docs/Web/API/fetch, “The global fetch() method starts the process of fetching a resource from the network, returning a promise that is fulfilled once the response is available.

The promise resolves to the Response object representing the response to your request.

A fetch() promise only rejects when a network error is encountered (which is usually when there’s a permissions issue or similar). A fetch() promise does not reject on HTTP errors (404, etc.). Instead, a then() handler must check the Response.ok and/or Response.status properties.”

So when addItem() is called the code will run synchronously (setNewItem() will not be executed) up to and including the await apiRequest() statement. While I don’t have the reference info. for it, I wrote some test code to understand at what point the setNewItem() function invocation statement would be executed, and I have shared that test program a little later in this section. From the test program, I understand that the await apiRequest() call will synchronously run apiRequest() code up to and including await fetch() statement in it. This fetch statement returns a promise (see above extract from fetch page reference). I think the usual scenario would be that this promise would be in pending state (and not yet resolved/rejected), and in this case, this line of execution will be suspended till the promise that the fetch() statement returns is resolved or rejected. After this suspension, the code that called addItem() continues and so setNewItem() function is executed. … Eventually the fetch promise will be resolved/rejected and at that point, the code on left hand side of await fetch statement (or catch block of try block in which fetch statement is) will start getting executed and apiRequest() function will return at which point the code in addItem() on left hand side await apiRequest statement will start executing. This line of execution will stop on addItem() function exiting.

The test code that I mentioned above is given below:

// Program run results are given at bottom of this file
function timeStampLog(entry, prm = null) {
  const d = new Date();
  console.log(
    `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} ${entry}`,
    prm ? prm : ""
  );
}
function fakeFetch() {
  timeStampLog("[Just after entering fakeFetch()]");
  timeStampLog("[Just before creating promise in fakeFetch()]");
  const prm = new Promise((resolve, reject) => {
    timeStampLog("[Just before calling setTimeout in Promise function]");
    setTimeout(() => {
      timeStampLog(
        "[Just before calling resolve or reject in Promise function]",
        prm
      );
      if (Math.random() < 0.5) resolve("fakeFetch Success Value");
      else reject("fakeFetch Failed");
      timeStampLog(
        "[Just after calling resolve or reject in Promise function]",
        prm
      );
    }, 2000);
    timeStampLog("[Just after calling setTimeout in Promise function]");
  });
  timeStampLog("[Just after creating promise in fakeFetch()]", prm);
  timeStampLog("[Just before returning created promise in fakeFetch()]", prm);
  return prm;
}
const apiRequest = async () => {
  let result = null;
  try {
    timeStampLog("[Before await fakeFetch()]");
    result = await fakeFetch();
    timeStampLog("[After await fakeFetch()]", result);
  } catch (err) {
    result = err;
  } finally {
    return result;
  }
};
const addItem = async () => {
  timeStampLog("[Before await apiRequest()]");
  const result = await apiRequest();
  timeStampLog("[After await apiRequest()]", result);
};
const handleSubmit = () => {
  timeStampLog("[Before addItem()]");
  addItem();
  timeStampLog("[After addItem()]");
};
handleSubmit();

/*
Program output for successful case (Math.random()< 0.5) is given below:
19:48:10 [Before addItem()]
19:48:10 [Before await apiRequest()]
19:48:10 [Before await fakeFetch()]
19:48:10 [Just after entering fakeFetch()]
19:48:10 [Just before creating promise in fakeFetch()]
19:48:10 [Just before calling setTimeout in Promise function]
19:48:10 [Just after calling setTimeout in Promise function]
19:48:10 [Just after creating promise in fakeFetch()] Promise { <pending> }
19:48:10 [Just before returning created promise in fakeFetch()] Promise { <pending> }
19:48:10 [After addItem()]
19:48:12 [Just before calling resolve or reject in Promise function] Promise { <pending> }
19:48:12 [Just after calling resolve or reject in Promise function] Promise { 'fakeFetch Success Value' }
19:48:12 [After await fakeFetch()] fakeFetch Success Value
19:48:12 [After await apiRequest()] fakeFetch Success Value
*/
/*
Program output for failure case (!(Math.random()< 0.5)) is given below:
19:47:59 [Before addItem()]
19:47:59 [Before await apiRequest()]
19:47:59 [Before await fakeFetch()]
19:47:59 [Just after entering fakeFetch()]
19:47:59 [Just before creating promise in fakeFetch()]
19:47:59 [Just before calling setTimeout in Promise function]
19:47:59 [Just after calling setTimeout in Promise function]
19:47:59 [Just after creating promise in fakeFetch()] Promise { <pending> }
19:47:59 [Just before returning created promise in fakeFetch()] Promise { <pending> }
19:47:59 [After addItem()]
19:48:1 [Just before calling resolve or reject in Promise function] Promise { <pending> }
19:48:1 [Just after calling resolve or reject in Promise function] Promise { <rejected> 'fakeFetch Failed' }
19:48:1 [After await apiRequest()] fakeFetch Failed
*/ 

Comments

Archive