JavaScript Loops
Pick the right loop in JavaScript with for, while, for-of, for-in, and array methods.
TL;DR
- 01Use
forandwhileloops for full counter control. - 02Use
for...offor array values andfor...infor object keys. - 03Use
map()andfilter()array methods for transformations.
Tips
- 01Prefer array methods like
map()andfilter()overforloops because they make your data transformations more readable. - 02Use
for...ofinstead offor...inon arrays becausefor...initerates keys as strings and inherits prototype properties.
Warnings
- 01Avoid modifying an array with
splice()orpush()while looping, since this skips elements and causes subtle index bugs. - 02Using
awaitinside aforEachcallback silently fails to pause, becauseforEachignores returned promises and runs callbacks concurrently.
For and While
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-of for Arrays
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-in for Objects
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}`);
}Async Loops
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 and Control
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]);
}In Practice
Iterates through store inventory to calculate cart totals and build order summaries while skipping out of stock items.
- 01Declare an inventory array and initialize variables to track the final total and name list.
- 02Iterate through each inventory item using a sequential
for...ofloop. - 03Skip items that are out of stock using a
continuestatement to prevent incorrect calculations. - 04Accumulate the price of in-stock items and record their names to finalize the order summary.
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);
}FAQ
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.