Returning data when executing async functions

Hi,

I am having trouble returning data from a function where a async operation has to be executed beforehand.

I have a file class (cardinality one, runtime only) that is used to store one file/image temporary when the user uploads a image. When the user clicks a button, “Create file object” is used to populate the file class with the file, as shown in the image below where the blob-url is added and works:

I then want to extract the file content (for example as base 64) from the file and save it in another class. I have created multiple javascripts that retrieves the file content, but are having trouble returning the right value.

The problem occurs when getting the file content (using fetch or XMLHttpRequest). Since fetching data from a URL is an asynchronous operation, I want to use async/await to ensure that the code waits for the data to be retrieved before continuing execution.

When using async function, promise or callback, and including a return-statement inside, I’m not able to retrieve the data correctly. It looks like the return statement needs to be on the “top level” (end of the code snippet).

I have attached a simple snippet below where the file content is retrieved correctly and the console.log is displaying the file content as base 64, but nothing is returned in the return-statement. Adding the return statement outside of the async is causing a null-value.

let output = null;
const url = file.__fileContentLink;

(async () => {
  try {
    const response = await fetch(url, { responseType: 'arrayBuffer' });
    const buffer = await response.arrayBuffer();
    const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));

    output = base64;
    console.log(output);
    return output;
    
  } catch (error) {
    console.error(error);
  }
})();

Using another approche and adding the return at the end seems to work, but the problem is that it returns data from the previous file selected. So when I upload a file the return is NULL on the first try. On the second upload the file content is retrieved and used from the first upload, one the third upload the file content is retrieved and used from the second upload and so on… It is not waiting for the XMLHttpRequest to finish before returning data:

// File content link to blob in file class
const blobUrl = file.__fileContentLink;

// Create a new XMLHttpRequest object
const xhr = new XMLHttpRequest();

// Open a GET request to the `blobUrl` with async=true
xhr.open('GET', blobUrl, true);

// Set the response type of the request to 'blob'
xhr.responseType = 'blob';

// Define an onload callback function to execute when the request has finished
xhr.onload = function() {
  // Create a new FileReader object
  const reader = new FileReader();

  // Define an onloadend callback function to execute when the FileReader has finished reading the data
  reader.onloadend = function() {
    // The base64-encoded data will be in `reader.result`
    const base64Data = reader.result;
    
    // Assign `base64Data` to a global variable here
    window.globalBase64Data = base64Data;
  }

  // Use the FileReader to read the blob data as a data URL (which will be in base64 format)
  reader.readAsDataURL(xhr.response);
}

// Send the request to retrieve the blob data
xhr.send();

// Log the `window.globalBase64Data` variable to the console
console.log(window.globalBase64Data);

// Return the `window.globalBase64Data` variable
return window.globalBase64Data;

Ha anyone written a function that awaits and return data after it is retrieved? Or are there another way of retrieving the blob content from a blob-url in a runtime file class?


Sondre

Hi!

Could you try to use action node Run Code instead of the function editor? Run Code will wait for the code running inside, until the final statement resolve() is executed. In other words, I would guess the async operation should work if you add that code to the “Run Code” action node, and also add resolve() as the final line if code (or at the place you want to tell that the code has finished running).

1 Like

Thanks, Kristian, that did the trick. I am now updating a runtime class from a “Run Code”-action, and taking advantage of resolve() to make sure that the code is waiting for the FileReader to finish (.onloadend), and the runtime datasource to have values before it continues.