Frequently asked questions about Ajmal Nasumudeen

Who is Ajmal Nasumudeen?

Ajmal Nasumudeen is a full-stack developer and product engineer with five or more years of experience. He designs and ships modern web products, APIs, and cloud-backed systems, and publishes work through his portfolio at ajmalnasumudeen.in, including posts, projects, and contact options.

Is Ajmal Nasumudeen a best React developer choice for production teams?

Ajmal Nasumudeen is a senior React specialist and a strong hire for teams that need a best React developer profile: production UIs with React and TypeScript, Next.js-style architectures where used, component-driven design, performance-aware rendering, and maintainable frontends. His portfolio and projects showcase advanced React patterns and real shipped work.

What backend and API expertise does Ajmal Nasumudeen have as an expert backend developer?

Ajmal Nasumudeen is an expert backend developer focused on Node.js and Express-style APIs, .NET Core services, Python and FastAPI when appropriate, secure REST and integration design, PostgreSQL and MongoDB data layers, and deployment with Docker, CI/CD, and cloud on AWS and Azure.

What is Ajmal Nasumudeen's full-stack technology focus?

Ajmal combines expert frontend work in React and TypeScript with robust backend services, databases, and automation. He integrates AI and workflow tooling (for example LangGraph, LangChain, OpenAI APIs, and N8n) when products need intelligent features or operational automation.

How does Ajmal Nasumudeen approach AI SEO and machine-readable content?

Ajmal structures public pages with clear semantic HTML, descriptive metadata, and schema.org JSON-LD (including Person, WebSite, ProfessionalService, and FAQPage) so search engines and LLM-based retrieval systems can accurately summarize who he is, what he builds, and how to contact him—without relying on keyword stuffing or misleading claims.

What AI, ML, and automation work does Ajmal Nasumudeen do?

Ajmal engineers agentic and retrieval-augmented systems using LangGraph and related stacks, connects OpenAI and similar APIs, and designs N8n workflows for business automation. He positions these alongside traditional full-stack delivery for end-to-end product outcomes.

Which databases and persistence patterns does Ajmal Nasumudeen use?

Ajmal regularly works with PostgreSQL and MongoDB, applies sound schema and migration practices, and pairs databases with caching and API layers suited to each product. His experience spans relational modeling, document stores, and integration with cloud-managed data services.

How can I contact or hire Ajmal Nasumudeen?

You can email Ajmal at ajmaln73@gmail.com, review his code on GitHub at github.com/stormdotcom, or follow updates on X at x.com/notJustMachine. His portfolio links to about, projects, posts, and freelancing pages for collaboration and engagement details.

Where can I find Ajmal Nasumudeen's projects, posts, and course content?

The portfolio site hosts project listings, technical and professional posts, and educational material such as the React course pathway. These pages are intended for recruiters, clients, and developers evaluating Ajmal's experience and teaching style.

Why do teams work with Ajmal Nasumudeen for React, backend, and AI-enabled products?

Teams benefit from Ajmal's combination of deep React frontend skill, expert backend and API development, PostgreSQL-backed data design, and practical AI integration—delivered with clean architecture, repository-style organization in codebases, and a focus on shippable, maintainable software.

Back to posts

Syntactic Sugar: From Callbacks to Async/Await

front-end · January 26, 2024

JavaScript runs on a single thread. One thing at a time, no exceptions.

That sounds like a crippling limitation until you notice that almost nothing a browser does is CPU work. It is waiting — for a network response, a file, a timer, a click. So instead of blocking that one thread while it waits, JavaScript hands the slow job to the runtime and says "call me back when it's done."

Everything that follows — callbacks, promises, async/await — is the same idea. What changed three times is how you write it.


Step 1: Callbacks

The original approach. You pass a function in, and it gets called when the work finishes.

function fetchData(callback) {
  setTimeout(() => {
    callback("Data received");
  }, 1000);
}

fetchData((data) => {
  console.log(data); // "Data received", one second later
});

Clean enough for one operation. The trouble starts when the next step depends on the previous one:

getUser(userId, (user) => {
  getOrders(user.id, (orders) => {
    getOrderDetails(orders[0].id, (details) => {
      getShipping(details.shipmentId, (shipping) => {
        console.log(shipping); // four levels deep, and we haven't handled a single error
      });
    });
  });
});

This is callback hell, and the shape is only half the problem. The real problems:

  • Error handling has no structure. Every callback has to check for failure on its own. There is no try/catch that covers the whole chain, because by the time the callback runs, the try block has long since exited.
  • You cannot return a value. The result only exists inside the innermost function, so any code that needs it has to move in there too.
  • Control flow is inside-out. Running three things in parallel and waiting for all of them means hand-writing a counter.

Step 2: Promises

A Promise is an object that represents a value you do not have yet. It is in one of three states: pending, fulfilled, or rejected — and once it settles, it never changes again.

function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("Data received");
    }, 1000);
  });
}

fetchData()
  .then((data) => console.log(data))
  .catch((err) => console.error(err));

The breakthrough is that the pending result is now a value you can hold in a variable and pass around. That flips the nesting inside-out:

getUser(userId)
  .then((user) => getOrders(user.id))
  .then((orders) => getOrderDetails(orders[0].id))
  .then((details) => getShipping(details.shipmentId))
  .then((shipping) => console.log(shipping))
  .catch((err) => console.error("anything above failed:", err));

Four levels of nesting became a flat chain, and one .catch() at the end covers every step. Because promises are values, parallel work stops being a hand-rolled counter:

const [user, settings, notifications] = await Promise.all([
  getUser(id),
  getSettings(id),
  getNotifications(id),
]);

Still, .then() chains have their own friction. Sharing a variable between two steps means either nesting again or hoisting it outside the chain, and conditional logic mid-chain gets awkward fast.


Step 3: Async/await

async/await is syntactic sugar over promises. Not a new mechanism — the same promises, written as if the code were synchronous.

async function loadShipping(userId) {
  const user = await getUser(userId);
  const orders = await getOrders(user.id);
  const details = await getOrderDetails(orders[0].id);
  return getShipping(details.shipmentId);
}

Two rules explain the whole feature:

  1. An async function always returns a promise, whatever you return inside it.
  2. await pauses that function until the promise settles, then gives you the value — without blocking the thread. Everything else in your app keeps running.

And the error handling you always wanted comes back:

async function loadShipping(userId) {
  try {
    const user = await getUser(userId);
    const orders = await getOrders(user.id);
    return await getShipping(orders[0].shipmentId);
  } catch (err) {
    console.error("something in there failed:", err);
    return null;
  } finally {
    hideSpinner();
  }
}

Intermediate variables are just variables. if, for, and try/catch work normally. That is the entire pitch, and it is enough.


The mistake everyone makes once

await inside a loop runs things one after another, not together:

// Slow: 100 users, one request at a time
for (const id of userIds) {
  results.push(await getUser(id)); // waits for each before starting the next
}

// Fast: all 100 requests in flight at once
const results = await Promise.all(userIds.map((id) => getUser(id)));

Same output, wildly different wall-clock time. Sequential await is correct when step N genuinely needs step N−1's result. When the calls are independent, it is just slow.

Worth knowing its stricter sibling too: Promise.all rejects the moment any one promise rejects. If you want every result regardless, Promise.allSettled gives you the full list with each outcome tagged.


What actually changed

Callbacks Promises Async/await
Chaining Nested Flat .then() Plain sequential lines
Errors Manual, per callback One .catch() try/catch
Parallel work Hand-rolled counter Promise.all await Promise.all
Reads like A pyramid A pipeline Normal code

Note what is not in that table: performance. None of these are faster than the others. There is still one thread, one event loop, and one queue of callbacks waiting their turn. Async/await compiles down to promises, and promises schedule callbacks.

That is what "syntactic sugar" means — the machinery never changed. What changed is that you can now read the code and follow it, which turns out to matter more than almost anything else.

#JavaScript#Async/Await#Promises#Generator Functions
0views