Frontend Development 11 min read

Refactoring Examples for Common Business Scenarios: From Callback Hell to Promise.all and Pure Functions

This article demonstrates how to refactor tangled asynchronous request chains and complex if‑else logic in JavaScript by replacing callback hell with Promise.all and async/await, extracting pure helper functions, and applying best‑practice principles to improve readability, testability, and maintainability.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Refactoring Examples for Common Business Scenarios: From Callback Hell to Promise.all and Pure Functions

In many business scenarios, code quickly becomes hard to read when requests depend on each other or when long if‑else chains are used. The article first presents a typical front‑end/Node example where functions A, B, C, and D must be called in a specific order, showing a callback‑hell implementation that nests calls and ignores possible concurrency.

function A(callback) {
  ajax(url, function(res) {
    callback(res);
  });
}
// ... similar for B, C, D
A(function(resa) {
  B(function(resb) {
    C(resb, function(resc) {
      D(resa, resb, resc, function(resd) {
        console.log("this is D result:", resd);
      });
    });
  });
});

The author explains that while this code works, it is inefficient because A and B could run in parallel. The evolution of asynchronous handling in JavaScript—from raw callbacks to Promise, generators, and finally async/await—is outlined, and the modern solution using Promise.all with async/await is provided.

function A() {
  return new Promise(r =>
    setTimeout(() => { r("a"); }, 2000)
  );
}
// ... similar for B, C, D
async function asyncBC() {
  const resb = await B();
  const resc = await c(resb);
  return { resb, resc };
}
async function asyncTask() {
  const [resa, { resb, resc }] = await Promise.all([A(), asyncBC()]);
  const resd = await D(resa, resb, resc);
  return resd;
}
asyncTask().then(resd => {
  console.log("this is D result:", resd);
});

Next, the article tackles a painful "if‑else hell" example that determines a page background color based on many global variables and configuration objects. The original monolithic function is shown, followed by a refactored version that extracts three pure helper functions, each handling a specific responsibility and receiving required data via parameters instead of accessing globals.

const getBackgroundMapBgColor = (pageUrlObj, config) => {
  if (!config.backgroundMap) return "";
  let queryParam = pageUrlObj.params;
  if (!queryParam.myPid) return "";
  const pids = queryParam.myPid.split("-");
  if (pids.length === 2) {
    return config.backgroundMap[pids[1]];
  }
  return config.backgroundMap[pids[0]];
};

const getCustomBgMapBgColor = () => {
  let bgColor = "";
  if (window.__customMyConfig.customBgMap) return "";
  Object.keys(window.__customMyConfig.customBgMap).forEach(item => {
    if (this.pageUrl.indexOf(item) !== -1) {
      bgColor = window.__customMyConfig.customBgMap[item];
    }
  });
  return bgColor;
};

const _getPageBgColor = (pageInfo, pageUrlObj) => {
  if (!window.__isMyCache && !window.__pageTheme) return "";
  if (window.__pageTheme) return window.__pageTheme.backgroundColor;
  if (pageInfo && pageInfo.theme) return pageInfo.theme.backgroundColor;
  let bgColor = getBackgroundMapBgColor(pageUrlObj, window.__customMyConfig);
  if (!bgColor) bgColor = getCustomBgMapBgColor();
  return bgColor;
};

The refactoring highlights two principles: extracting logical units into separate functions and adopting an "error‑first" return strategy, which together make the code easier to understand, test, and maintain. The author also notes that the new functions still rely on global state, suggesting further improvement by passing all dependencies as arguments to achieve pure functions.

Additional common problems are listed, such as logic coupling in the view layer, duplicated code, functions with multiple responsibilities, unclear parameter lists, magic numbers/strings, and poor naming or data structures. The article then discusses why code optimization matters—technical growth, company requirements, team collaboration, rapid iteration, and personal reputation.

Finally, practical advice is offered: understand business requirements deeply, study open‑source projects and best practices, treat code as a human‑readable artifact, balance code quality with delivery speed, perform thorough code reviews, prioritize early error returns, split code into independent pure functions, use state‑maps or state machines for complex branching, learn design patterns, and refactor continuously before problems become entrenched.

In summary, the piece uses concrete examples to illustrate how modern JavaScript features and disciplined refactoring can transform messy, hard‑to‑maintain code into clean, testable, and extensible implementations.

frontendjavascriptcode qualityrefactoringAsync/AwaitPromisepure functions
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.