Pick the right loop in JavaScript with for, while, for-of, for-in, and array methods.
for and while loops for full counter control.for...of for array values and for...in for object keys.map() and filter() array methods for transformations.map() and filter() over for loops because they make your data transformations more readable.for...of instead of for...in on arrays because for...in iterates keys as strings and inherits prototype properties.splice() or push() while looping, since this skips elements and causes subtle index bugs.await inside a forEach callback silently fails to pause, because forEach ignores returned promises and runs callbacks concurrently.forRuns a block of code a set number of times using an initialized counter.
for (let i = 0; i < 5; i++) {
console.log(i);
}whileRepeatedly executes a block of code as long as a specified condition remains true.
let n = 0;
while (n < 3) {
console.log(n);
n++;
}do...whileRuns a block of code once before checking if the loop condition is true.
let i = 0;
do {
console.log(i);
i++;
} while (i < 3);break / continueUse break to exit a loop immediately and continue to skip the current iteration.
for (let i = 0; i < 5; i++) {
if (i === 2) continue;
if (i === 4) break;
console.log(i);
}for...ofIterates directly over iterable values like array elements without manual index management.
const nums = [10, 20, 30];
for (const n of nums) {
console.log(n);
}Iterable objectsWorks natively on strings, sets, maps, and other built-in iterable structures.
for (const char of "Hi") {
console.log(char);
}DestructuringUnpacks properties directly within the loop declaration for cleaner object access.
const users = [{ name: "Ada" }, { name: "Bob" }];
for (const { name } of users) {
console.log(name);
}entries()Returns index-value pairs to retrieve the loop counter alongside the element.
const items = ["a", "b"];
for (const [index, val] of items.entries()) {
console.log(index, val);
}for...inIterates over the enumerable string property keys of an object.
const user = { name: "Ada", age: 30 };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}hasOwnProperty()Filters out inherited properties to iterate only the object's own direct properties.
for (const key in user) {
if (user.hasOwnProperty(key)) {
console.log(key);
}
}Object.entries()Converts object properties into key-value arrays for cleaner, modern iteration.
const user = { name: "Ada", age: 30 };
for (const [key, val] of Object.entries(user)) {
console.log(`${key}: ${val}`);
}for...of with awaitExecutes async tasks sequentially, pausing the loop for each promise to resolve.
for (const id of ids) {
const res = await fetchItem(id);
console.log(res);
}forEach warningAvoid await in forEach; it ignores returned promises and fires them concurrently.
// Broken: will not wait for save to complete
items.forEach(async item => {
await save(item);
});Promise.all()Runs all async operations in parallel by mapping items to an array of promises.
const apiCall = id => fetchItem(id);
const results = await Promise.all(
ids.map(apiCall)
);for await...ofIterates over async iterables, waiting for each value to resolve sequentially.
for await (const chunk of readStream()) {
console.log(chunk);
}Nested loopsRuns a loop inside another loop; plain break only escapes the inner loop.
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 3; j++) {
console.log(`${i},${j}`);
}
}Labeled breakExits multiple levels of nesting at once by referencing a statement label.
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (i === 1) break outer;
}
}Loop performanceCache array length before the loop to avoid querying it on every iteration.
const len = items.length;
for (let i = 0; i < len; i++) {
console.log(items[i]);
}The for...of loop iterates over iterable values like arrays, strings, Set, or Map. In contrast, for...in iterates over an object's enumerable property keys as strings. Use for...of when you want element values and for...in when you need to walk property keys.
The await keyword works correctly inside for, for...of, and while loops, running each iteration sequentially. Avoid using await inside forEach because it ignores returned promises and runs callbacks concurrently without waiting. Use a for...of loop instead.
Label the outer loop as outer:. Call break outer inside the inner loop to exit both levels at once. Alternatively, extract the nested loops into a separate function and use a return statement to exit early.
The for...in loop enumerates all inherited enumerable properties in addition to direct properties. Use Object.keys(obj) to retrieve only the object's own enumerable keys as an array. Alternatively, add an hasOwnProperty() guard check inside the loop.
Prefer map() when producing a new array of the same length with transformed values, and filter() when selecting a subset. Both express intent clearly. Reach for a for or for...of loop when you need to break early or accumulate a non-array result.
Processing Shopping Cart Items
Iterates through store inventory to calculate cart totals and build order summaries while skipping out of stock items.
for...of loop.continue statement to prevent incorrect calculations.const items = [
{ name: "Book", price: 15, stock: 4 },
{ name: "Pen", price: 2, stock: 0 },
{ name: "Laptop", price: 800, stock: 2 }
];
let cartTotal = 0;
const orderSummary = [];
for (const item of items) {
if (item.stock === 0) continue;
cartTotal += item.price;
orderSummary.push(item.name);
}Use continue to skip invalid or out-of-stock data without breaking the entire loop execution.