mongodb 7.5.0MongoG Query Scripts and Automatic Await
Learn how MongoG waits for driver Promises across assignments, conditions, helpers, loops, errors, and explicit parallel work without changing your source.
Write direct, sequential scripts
MongoDB driver operations are asynchronous. MongoG automatically waits where a value is needed, so a query script can stay concise without losing execution order:
use("mongog_tutorial");
const users = db.collection("users");
const user = users.findOne({ name: "Ada" });
print(user?.name);
if (users.countDocuments({ active: true }) > 0) {
print("Active users exist");
}
The assignment receives the resolved document. The condition receives the resolved count. The next top-level statement does not race ahead of an unfinished operation.
MongoG performs this transformation with the TypeScript syntax tree. The editor, saved scripts, and history keep exactly the source you wrote, and errors map back to the original lines.
Explicit await remains valid
Existing asynchronous JavaScript works normally:
const user = await users.findOne({ name: "Ada" });
try {
await users.updateOne(
{ _id: user._id },
{ $set: { lastSeenAt: new Date() } },
);
} catch (error) {
console.error("Update failed", error);
}
Use explicit await when it makes intent clearer, when sharing code with another Node.js environment, or when a library API requires an explicitly async callback.
Helpers, loops, and array callbacks
Ordinary functions can contain database work:
function userName(id) {
return users.findOne({ _id: id })?.name;
}
const names = ids.map(id => userName(id));
printjson(names);
MongoG adapts common array callbacks including map, forEach, filter, reduce, find, some, every, and flatMap. These callbacks wait sequentially. The adaptation exists only inside the script VM; it does not modify the host application or driver prototypes.
For cursor iteration, use for...of:
for (const user of users.find({ active: true })) {
print(user.name);
}
MongoG uses the cursor's async iterator and closes it on early exit. A driver cursor.forEach() callback does not automatically await a Promise returned by the callback, so prefer for...of when the loop performs database work.
Start independent work explicitly
Separate assignments run in sequence. Use a Promise combinator when operations are independent and should start together:
const [activeCount, archivedCount] = Promise.all([
users.countDocuments({ active: true }),
users.countDocuments({ archived: true }),
]);
printjson({ activeCount, archivedCount });
Promise.allSettled, Promise.race, and Promise.any also preserve their normal parallel behavior. Do not use parallel operations within one MongoDB transaction session; the Node.js driver does not support that pattern.
Understand synchronous boundaries
Some JavaScript contexts cannot suspend. Constructors, setters, synchronous generators, parameter defaults, class field initializers, and sort comparators must stay synchronous. Resolve database values before entering them:
const priority = users.findOne({ name: "Ada" })?.priority;
const rows = [{ name: "task", priority }];
rows.sort((left, right) => left.priority - right.priority);
If a Promise reaches a synchronous-only boundary, MongoG reports an explanatory error instead of silently using the wrong value.
Errors and cancellation
An uncaught failure stops later statements. A rejection inside try can be handled with catch, and finally finishes before the helper or script continues. Cancellation checks cannot be swallowed by a user catch block.
Parallel work remains tracked until underlying operations settle, including losing race inputs and remaining jobs after a rejected all.
Recognize statement result boundaries
MongoG instruments executable statements rather than treating the entire editor as one opaque Promise. Each top-level statement can produce its own result and associated console output.
const count = users.countDocuments({ active: true });
print("Active users:", count);
users.find({ active: true }).sort({ name: 1 });
The first assignment resolves before count is used. The print output belongs to its statement, and the final cursor becomes a paged result. A helper’s internal expressions still contribute to the result of the top-level statement that called it.
Automatic await does not rewrite the source saved to history or shown in the editor. Syntax and runtime errors map back to the original source locations.
Keep portable code explicit where useful
MongoG’s concise style is ideal for interactive work, but code copied into an application should preserve normal JavaScript asynchronous semantics. Add explicit await, mark helper functions async, and choose concurrency intentionally before moving a script into a service or migration.
Compare these two forms in MongoG:
// Interactive concise form
const one = users.findOne({ name: "Ada" });
// Portable explicit form
const two = await users.findOne({ name: "Grace" });
printjson({ one: one?.name, two: two?.name });
Both work in MongoG. Only the second communicates normal Node.js behavior when copied without MongoG’s instrumentation.
Practice sequential and parallel work
Measure three independent countDocuments() operations first as adjacent assignments and then inside Promise.all(). Verify that both forms return the same values. Next, run two dependent operations where the second needs the first result and explain why parallel execution would be incorrect.
Finish by placing a database call inside a sort comparator and observe the synchronous-boundary error. Refactor it by resolving all comparison data before calling sort().
Common mistakes
- Assuming two adjacent assignments run in parallel is incorrect; use an explicit Promise combinator.
- Running asynchronous database work inside
Array.sort()cannot work because the comparator is synchronous. - Using cursor
forEach()for awaited writes can let callbacks outlive the cursor operation. - Removing every explicit
awaitfrom portable application code can make that code misleading outside MongoG. - Assuming automatic await changes the saved source can make debugging expectations incorrect; instrumentation is an execution-time transformation.
- Using
Promise.all()for dependent operations can race on values or database state even when the syntax is valid.
Continue learning
Explore the globals, BSON helpers, and execution modes available to every script. MongoG's behavior preserves the asynchronous contract described in the official Node.js driver documentation.