skip to Main Content

I have just come across an anonymous self-invoking JavaScript function and am unsure as to what the point is. I have read a couple of posts about how it is to do with scope, but it still doesn’t make total sense to me.

The example in question:

const express = require("express");
const axios = require("axios");
const redis = require("redis");

const app = express();
const port = process.env.PORT || 3000;

let redisClient;

(async () => {
  redisClient = redis.createClient();

  redisClient.on("error", (error) => console.error(`Error : ${error}`));

  await redisClient.connect();
})();

async function fetchApiData(species) {
  ...
}
...

What is the difference between this

let redisClient;

(async () => {
  redisClient = redis.createClient();

  redisClient.on("error", (error) => console.error(`Error : ${error}`));

  await redisClient.connect();
})();

and just doing the following

let redisClient;
redisClient = redis.createClient();
redisClient.on("error", (error) => console.error(`Error : ${error}`));
await redisClient.connect();

2

Answers


  1. You can’t use await outside of a function — this is called "top-level await". Yes, browser DevTools will let you do that in their JS consoles, but regular scripts cannot in either browsers or the Node environment.

    So, the difference between sample A and sample B is that sample B will not work and sample A will.

    Login or Signup to reply.
  2. The reason you can’t do

    let redisClient;
    redisClient = redis.createClient();
    redisClient.on("error", (error) => console.error(`Error : ${error}`));
    await redisClient.connect();
    

    is that you can’t await on the top level – in the CommonJS module system. It’s possible in ESM, but not in Node’s default CommonJS.

    That said, the original code does look pretty odd. It looks like it was written by someone who may not have known how to turn await into a .then – an async IIFE like that that only awaits a single Promise seems odd, especially since there’s nothing that comes after the await in the IIFE. The original code is effectively the same as

    const redisClient = redis.createClient();
    redisClient.on("error", (error) => console.error(`Error : ${error}`));
    redisClient.connect(); // dangling Promise
    

    The original code creates a dangling Promise too, because the Promise returned by the IIFE isn’t caught. In both codes, it would be better to .catch the possible rejection of the .connect call. (Possible unhandled rejections should always be avoided – even if there’s also an error listener on redisClient.)

    const redisClient = redis.createClient();
    redisClient.on("error", (error) => console.error(`Error : ${error}`));
    redisClient.connect()
      .catch((error) => {
        // either do error handling here, or simply do nothing
        // if the error handler above is sufficient
        // but don't leave this `.catch` out
      });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search