Skip to content

refactor: stop awaiting promises one by one in loops - #1545

Merged
bobbyquantum merged 4 commits into
mainfrom
bobbyquantum/sonar-await-in-loop
Sep 29, 2026
Merged

bobbyquantum merged 4 commits into
mainfrom
bobbyquantum/sonar-await-in-loop

Conversation

@bobbyquantum

Copy link
Copy Markdown
Owner

Summary

SonarCloud added typescript:S9382 (await inside a loop) and reports typescript:S7503 (async function without await). Existing code in touched files trips both and fails the quality gate on PRs. This clears them across frontend/src/app and backend/src (generated api-client excluded), without disabling any rule.

An AST sweep of the tree finds 0 remaining hits for either pattern (141 loops and 106 async functions before).

How loops were handled

New helpers, with unit tests (frontend/src/app/utils/sequential.ts, backend/src/utils/sequential.ts): forEachSequential, mapSequential, firstResultSequential, forEachPage (cursor pagination), plus chunk and forEachConcurrent on the frontend. They express "one at a time" as an explicit promise chain, and each call site says in a comment why it is sequential. mapWithConcurrency (backend) now recurses instead of looping.

Became concurrent (independent items, order of results preserved):

  • Backend: getElementImageUrls (reference image lookups), FileStorageService directory size / listProjectFiles stats / deleteSlotImage, account-deletion profile image cleanups, MCP worldbuilding search reads and tombstone lookups (both bounded to 8 via mapWithConcurrency).
  • Frontend: media selector thumbnails, era image cache lookups (timeline), cover-sync cache checks, verifyLocalAvailability, delete-project-databases in the home page and project settings.
  • Existing worker-pool loops (media download, cover download, MCP worldbuilding read) now use forEachConcurrent / mapWithConcurrency.

Kept sequential: IndexedDB work on one database (storage-context clone/rekey/rename/clear, migration, snapshots, project cache), Yjs document opens, ordered import/export/publish/EPUB/PDF/HTML/Markdown generation (chapter numbers, shared image caches, filenames assigned in order), progress reporting per item, remote-store sync (Dropbox/Nextcloud, rate limited), server calls with per-item error handling, batches (chunk then sequential batches, each batch still concurrent), DO/D1/R2 batched writes, activity event writes, and everything with early exit (firstResultSequential) or cancellation. Cursor pagination, polling, stream reading, retry loops and the interactive password prompt are inherently sequential and are now recursive/forEachPage.

The reset page keeps its database deletions sequential (its spec mocks a single shared delete request).

S7503

async dropped where nothing awaits, returning Promise.resolve/reject for plain values. Where the body can throw synchronously and callers rely on a rejection (a handful of functions), async is kept and the returned promise is return awaited, so behaviour is unchanged.

AGENTS.md has a short note on the patterns.

Testing

  • Frontend: npm run test:coverage 394 files / 9420 tests pass, coverage above thresholds; npm run lint clean.
  • Backend: bun test 1797 pass; eslint src no errors (existing warnings only); tsc shows only the errors already present on main.
  • Note: with isolate: false, running storage-context.service.spec.ts in the same worker after certain page specs can time out on main as well (reproduced on an unmodified checkout); the full suite is green.

Fixes SonarCloud typescript:S9382 (await inside a loop) and
typescript:S7503 (async function without await) across the frontend and
backend.

Loops that must stay ordered now go through small helpers in
utils/sequential.ts (forEachSequential, mapSequential,
firstResultSequential, forEachPage; chunk and forEachConcurrent on the
frontend). Independent work uses Promise.all or a bounded pool.
Async functions without await drop async and return an explicit promise,
or keep async with return await where a synchronous throw must still
reject.
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository: bobbyquantum/inkweld/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: d9139bf6-4b99-4a97-a09f-a308b51e98d1


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bobbyquantum
bobbyquantum deployed to sonarcloud-analysis September 29, 2026 19:30 — with GitHub Actions Active
@bobbyquantum bobbyquantum changed the title Fix SonarCloud S9382 and S7503 across frontend and backend refactor: stop awaiting promises one by one in loops Sep 29, 2026
The frontend and backend each carried an identical copy of the sequential
promise helpers, which SonarCloud reports as duplication. Move them into a
shared workspace package, wired up like @inkweld/presence (tsconfig path
aliases, package tests in CI, coverage for SonarCloud), and fold the two
Fal.ai model listings into one category-parameterised function.
@bobbyquantum
bobbyquantum deployed to sonarcloud-analysis September 29, 2026 20:41 — with GitHub Actions Active
@bobbyquantum
bobbyquantum deployed to sonarcloud-analysis September 29, 2026 21:01 — with GitHub Actions Active
Merge the duplicate @inkweld/async import, write firstResultSequential's
guard as an early return, and build the EPUB worldbuilding sections from
one array literal instead of repeated pushes.
@bobbyquantum
bobbyquantum deployed to sonarcloud-analysis September 29, 2026 21:20 — with GitHub Actions Active
@sonarqubecloud

Copy link
Copy Markdown

@bobbyquantum
bobbyquantum merged commit 93164c0 into main Sep 29, 2026
52 of 53 checks passed
@bobbyquantum
bobbyquantum deleted the bobbyquantum/sonar-await-in-loop branch September 29, 2026 22:39

This branch was successfully deployed

1 active deployment
sonarcloud-analysis — 56e44e0c Deployed Sep 29, 2026 by bobbyquantum via SonarCloud Code Analysis #5276
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant