Replace callbacks by async/await

Replace callbacks by async/await for better readability and error handling in JavaScript.

You can query a model directly and test the results returned when using different parameter values with the Cloud console, or by calling the Vertex AI API directly.

Prompt Freeform

Refactor this JavaScript code to use async/await instead of callbacks for handling asynchronous operations. The goal is to improve code readability and error handling.

function fetchData(url, callback) {
  http.get(url, function(response) {
    let data = '';
    response.on('data', (chunk) => {
      data += chunk;
    });
    response.on('end', () => {
      callback(null, data);
    });
  }).on('error', (err) => {
    callback(err);
  });
}

Response

Of course! Refactoring this code to use async/await is an excellent way to make it more modern, readable, and easier to maintain.

The core challenge is that the native Node.js http module is based on callbacks and streams, not Promises. To use async/await, we first need to wrap the callback-based logic in a function that returns a Promise.

Refactored Code using async/await

Here is the refactored code. It consists of two parts: 1. A new fetchDataAsync function that returns a Promise. 2. An example main function showing how to use it with async/await.

// We need the 'http' or 'https' module. This example handles both.
const http = require('http');
const https = require('https');

/**
 * Fetches data from a URL and returns a Promise that resolves with the data.
 * This function wraps the native Node.js http/https module in a Promise.
 *
 * @param {string} url The URL to fetch data from.
 * @returns {Promise<string>} A promise that resolves with the response body.
 */
function fetchDataAsync(url) {
  // Determine which module to use based on the URL protocol
  const client = url.startsWith('https') ? https : http;

  return new Promise((resolve, reject) => {
    client.get(url, (response) => {
      // --- Improved Error Handling ---
      // Check for non-successful status codes (e.g., 404, 500)
      const { statusCode } = response;
      if (statusCode < 200 || statusCode >= 300) {
        // Consume response data to free up memory before rejecting.
        response.resume();
        return reject(new Error(`Request Failed. Status Code: ${statusCode}`));
      }

      let data = '';
      response.setEncoding('utf8'); // Good practice to set encoding

      // A chunk of data has been received.
      response.on('data', (chunk) => {
        data += chunk;
      });

      // The whole response has been received. Resolve the promise.
      response.on('end', () => {
        resolve(data);
      });

    }).on('error', (err) => {
      // This handles network errors (e.g., DNS resolution, connection refused).
      reject(err);
    });
  });
}

// --- Example Usage with async/await ---

// We create an async function to use the 'await' keyword.
async function main() {
  const url = 'https://jsonplaceholder.typicode.com/todos/1'; // Using a real API for testing

  console.log(`Fetching data from ${url}...`);

  try {
    // 'await' pauses the function until the promise is resolved or rejected.
    const responseData = await fetchDataAsync(url);
    
    // If the promise resolves, the value is assigned to responseData.
    const parsedData = JSON.parse(responseData); // Often, you'll want to parse JSON.
    console.log('Data received:', parsedData);

  } catch (error) {
    // If the promise is rejected, the error is caught here.
    console.error('An error occurred:', error.message);
  }
}

// Run the example
main();

Key Improvements and Explanation

  1. Promise Wrapper (fetchDataAsync):

    • async/await is syntactic sugar for Promises. Since the original http.get is callback-based, we must first "promisify" it.
    • The fetchDataAsync function returns a new Promise().
    • Inside the promise, we run the original http.get logic.
    • Instead of callback(null, data), we now call resolve(data) on success.
    • Instead of callback(err), we now call reject(err) on failure.
  2. Readability (async function main):

    • The logic inside the main function now reads like synchronous code: "try to await the data, and if you get it, log it; otherwise, catch the error."
    • This top-to-bottom, linear flow is much easier to follow than the nested callbacks and event listeners of the original code.
  3. Improved Error Handling:

    • try...catch Block: This is the standard, idiomatic way to handle errors with async/await. It cleanly separates the "happy path" (the try block) from the error-handling logic (the catch block). It catches both network errors and HTTP status code errors that we added.
    • Status Code Check: The original code would have treated an HTTP 404 Not Found or 500 Internal Server Error as a success, because the request itself didn't fail. The refactored version explicitly checks if statusCode is in the 2xx range and rejects the promise if it's not, which is much more robust.
Model: gemini-2.5-pro
Temperature: 0.2
Max output tokens: 8192
TopK: 40.0
TopP: 0.95