Include the condition inside of while(...)
expression into the lexical scope of do {...}
block in a manner that threats following code as a valid:
do {
const shouldQuit = getShouldIQuit();
} while (!shouldQuit)
Author(s): Vsevolod Rodionov
Stage: -1
Why is this important to have in the JavaScript language?
Unlike for()
, for...of
and for...in
blocks, who are iterating over known entity, while
and do...while
blocks are commonly used when break condition is unclear.
Often the break condition is actually defined inside the block; however, there is no common way to declare the exit condition that is defined inside the block clearly.
2 most common approaches are:
let shouldQuit = false;
do {
shouldQuit = getShouldIQuit()
} while (!shouldQuit)
and
while (true) {
const shouldQuit = getShouldIQuit();
if (shouldQuit) {
break;
}
}
Both of them have issues:
Option 1 is mutating the external state, is verbose and is can pollute the higher-level lexical scope with variable only needed for inner scope. This is still declarative approach: code states, that it has a computation block, and it has an break post-condition.
Option 2 has 3 issues:
- despite the fact that it has post-condition, it is declared in pre-condition manner (
while () {}
instead ofdo {} while ()
) - it is using
break
. Usage of this statement is controversial and discussed a lot, e.g.: 1, 2, 3, 4 - its approach us redundantly imperative: it increases cognitive complexity; instead of clearly stated break condition the one who sees this block for a first time has to look though it to discover the
break
control flow statements
// before
while (true) {
const tagCloseStart = this._cursor.clone();
const foundEndMarker = endMarkerPredicate();
this._cursor = tagCloseStart;
if (foundEndMarker) {
break;
}
parts.push(this._readChar(decodeEntities));
}
// after
do {
const tagCloseStart = this._cursor.clone();
const foundEndMarker = endMarkerPredicate();
this._cursor = tagCloseStart;
if (!foundEndMarker) {
parts.push(this._readChar(decodeEntities));
}
} while (!foundEndMarker)
// before
while (true) {
command = `${npmPath} install --ignore-scripts ${(toSlice === packageNames.length ? packageNames : packageNames.slice(sliceStart, sliceStart + toSlice)).join(" ")} --save-dev --user-agent="typesInstaller/${tsVersion}"`;
if (command.length < 8000) {
break;
}
toSlice = toSlice - Math.floor(toSlice / 2);
}
// after
do {
command = `${npmPath} install --ignore-scripts ${(toSlice === packageNames.length ? packageNames : packageNames.slice(sliceStart, sliceStart + toSlice)).join(" ")} --save-dev --user-agent="typesInstaller/${tsVersion}"`;
toSlice = toSlice - Math.floor(toSlice / 2);
} while (command.length >= 8000)
// before
var change = true
while (change) {
change = false
for (var i = 0; i < aliasArrays.length; i++) {
for (var ii = i + 1; ii < aliasArrays.length; ii++) {
if (intersect.length) {
change = true
}
}
}
}
// after
do {
let change = false;
for (var i = 0; i < aliasArrays.length; i++) {
for (var ii = i + 1; ii < aliasArrays.length; ii++) {
if (intersect.length) {
change = true
}
}
}
} while (change)
Babel plugin for this proposal can be implemented in a short period of time.