v156 · feature detection · migration
Feature detection & fallback
New syntax cannot be feature-detected with typeof or in — the file will not parse at all on an old engine, taking the whole script down with it. This page shows the correct pattern: probe the grammar in isolation, then branch to either the real feature or a labelled, honest fallback.
The probe
Checking…
// You cannot write `if (supportsImportDefer)` inline: a file containing
// `import defer` throws a SyntaxError before any of it runs. So keep the
// syntax in a SEPARATE module and import it dynamically, catching the parse:
async function supportsImportDefer() {
try {
await import("data:text/javascript,import defer * as n from" +
" \"data:text/javascript,export const ok=true\";export{}");
return true; // grammar parsed
} catch (e) {
if (e instanceof SyntaxError) return false; // not understood here
return true; // parsed, but the probe module failed to load
}
}
Branch to the right path
Detecting…
Enable the real feature
import defer is a V8 staged feature. Relaunch Chrome or Chrome Canary from a terminal with:
--js-flags=--js-defer-import-eval— the exact V8 flag (js_defer_import_eval), or--js-flags=--harmony— all staged JavaScript features.
Not a Blink feature: there is no chrome://flags entry, no --enable-blink-features name, and no origin trial. Reload this page after relaunching and the probe will flip to "supported".
Migration shape
Because the fallback is dynamic import(), the deferred code path should be written so both branches converge on the same asynchronous-friendly call site during migration. Once import defer is your baseline, the synchronous form removes the await at every caller:
let inspector;
if (await supportsImportDefer()) {
inspector = (await import("./defer-entry.js")).ns; // synchronous first-use later
} else {
inspector = null; // load lazily via import() on use
}
async function open(selection) {
const mod = inspector ?? await import("./inspector.js");
mod.open(selection);
}