oxc-project/oxc · warning

Prefer `async`/`await` to the callback pattern

Error message

Prefer `async`/`await` to the callback pattern

What it means

Diagnostic from oxlint's port of eslint-plugin-promise's prefer-await-to-callbacks style rule. It reports three callback shapes: calls to a function named `cb`/`callback`, function declarations whose LAST parameter is named `cb`/`callback` (callback-taking definitions), and calls whose last argument is a function whose FIRST parameter is named `err`/`error` when the call is not already inside await/yield. Event wiring (on/once/addEventListener/removeEventListener), array iterators (map/filter/forEach/some/every/find) and lodash-style `_.map` are exempted.

Source

Thrown at crates/oxc_linter/src/rules/promise/prefer_await_to_callbacks.rs:13

use oxc_ast::{
    AstKind,
    ast::{Argument, Expression, FormalParameters, MemberExpression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_semantic::NodeId;
use oxc_span::{GetSpan, Span};

use crate::{AstNode, context::LintContext, rule::Rule};

fn prefer_await_to_callbacks(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Prefer `async`/`await` to the callback pattern")
        .with_help("Refactor to use an `async` function with `await` instead of passing callbacks for cleaner error handling and control flow.")
        .with_label(span)
}

#[derive(Debug, Default, Clone)]
pub struct PreferAwaitToCallbacks;

declare_oxc_lint!(
    /// ### What it does
    ///
    /// The rule encourages the use of `async/await` for handling asynchronous code
    /// instead of traditional callback functions. `async/await`, introduced in ES2017,
    /// provides a clearer and more concise syntax for writing asynchronous code,
    /// making it easier to read and maintain.
    ///
    /// ### Why is this bad?
    ///
    /// Using callbacks can lead to complex, nested structures known as "callback hell,"

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Refactor the call site to an awaited promise: promisify the API or use its .async/.promises variant, then `const data = await doThing(arg)`.
  2. Refactor the definition to return a Promise instead of taking a callback: `async function getData(id) { ... }`.
  3. If the callback's first parameter is not an error, rename it (e.g. `(_, result)` or a meaningful name).
  4. For irreducibly callback-based APIs (event emitters, array iteration are already exempt), disable the rule or suppress inline.

Example fix

// before
fetchData((err, data) => {
  if (err) throw err;
  render(data);
});

// after
const data = await fetchData();
render(data);
Defensive patterns

Strategy: validation

Validate before calling

oxlint --promise-plugin src/ # prefer-await-to-callbacks

Try / catch

// when a callback API is unavoidable at the edge, contain it:
const fetchData = () =>
  new Promise((resolve, reject) => {
    api.call((err: Error | null, data: unknown) =>
      err ? reject(err) : resolve(data));
  });
// callers then `await fetchData()`

Prevention

When it happens

Trigger: Per the run() visitor: `cb()` / `callback()` callee identifiers; `function getData(id, callback) {}` or `const f = (cb) => {}` last-param named cb/callback; `heart(function(err) {})` / `customMap(errors, (err) => ...)` — a call with a trailing function whose first param is err/error, not under an await/yield ancestor. Passing `something => {}` (non-err name) does not fire; `.map(err => ...)`, `socket.on("error", err => ...)` are exempt.

Common situations: Codebases mid-migration from callback APIs (older Node libs, AWS SDK v2, redis clients) to promise APIs; wrapping libraries that only offer callbacks; callbacks that are not error-first (renaming the param avoids the report).

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/351669f9bde61a80. Report an issue: GitHub.