JavaScript ਦੇ async/await syntax ਨੂੰ ਸਾਨੂੰ callback hell ਤੋਂ ਬਚਾਉਣ ਲਈ ਬਣਾਇਆ ਗਿਆ ਸੀ। ਪਰ ਇਸ ਦੀ ਬਜਾਏ, ਇਸ ਨੇ ਇੱਕ ਸ਼ਾਂਤ ਪਰ ਵਧੇਰੇ ਖ਼ਤਰਨਾਕ ਸਮੱਸਿਆ ਪੈਦਾ ਕਰ ਦਿੱਤੀ: ਅਜਿਹਾ ਕੋਡ ਜੋ ਦੇਖਣ ਵਿੱਚ ਸਹੀ ਲੱਗਦਾ ਹੈ ਪਰ ਅਣਪਛਾਤੇ ਤਰੀਕੇ ਨਾਲ ਕੰਮ ਕਰਦਾ ਹੈ। ਤੁਸੀਂ ਫੰਕਸ਼ਨ ਬਾਡੀ ਵਿੱਚ await ਨੂੰ ਦੇਖਦੇ ਹੋ ਅਤੇ ਮੰਨ ਲੈਂਦੇ ਹੋ ਕਿ ਸਭ ਕੁਝ ਲਾਈਨ-ਦਰ-ਲਾਈਨ ਰੁਕ ਜਾਵੇਗਾ। ਅਕਸਰ ਅਜਿਹਾ ਨਹੀਂ ਹੁੰਦਾ। ਲੂਪਸ (Loops) ਤੇਜ਼ੀ ਨਾਲ ਅੱਗੇ ਵਧ ਜਾਂਦੇ ਹਨ। ਇੱਕ ਫੇਲ ਹੋਈ ਰਿਕੁਐਸਟ (request) ਕਾਰਨ ਪੂਰਾ ਬੈਚ (batch) ਫੇਲ ਹੋ ਜਾਂਦਾ ਹੈ। ਐਂਟਰੀ ਫਾਈਲਾਂ (Entry files) ਵਿੱਚ ਬਿਨਾਂ ਕਿਸੇ ਕਾਰਨ ਅਜੀਬ async wrappers ਪੈਦਾ ਹੋ ਜਾਂਦੇ ਹਨ। ਜੇਕਰ ਤੁਸੀਂ ਇਹਨਾਂ ਵਿੱਚੋਂ ਕਿਸੇ ਦਾ ਵੀ ਸਾਹਮਣਾ ਕੀਤਾ ਹੈ, ਤਾਂ ਇਹ ਤਿੰਨ ਪੈਟਰਨ (patterns) ਇਹਨਾਂ ਨੂੰ ਠੀਕ ਕਰ ਦੇਣਗੇ।

forEach ਦੇ ਅੰਦਰ await ਦੀ ਵਰਤੋਂ ਕਰਨਾ ਬੰਦ ਕਰੋ

ਇੱਥੇ ਇੱਕ ਆਮ ਗਲਤੀ ਹੈ ਜੋ ਦੇਖਣ ਵਿੱਚ ਨੁਕਸਾਨਦੇਹ ਨਹੀਂ ਲੱਗਦੀ:

const urls = ['/api/user', '/api/posts', '/api/comments'];

urls.forEach(async (url) => {
  const res = await fetch(url);
  const data = await res.json();
  console.log(data);
});

console.log('All done!');

ਇਸ ਨੂੰ ਚਲਾਓ, ਅਤੇ ਇੱਕ ਵੀ ਰਿਸਪਾਂਸ (response) ਆਉਣ ਤੋਂ ਪਹਿਲਾਂ ਹੀ 'All done!' ਪ੍ਰਿੰਟ ਹੋ ਜਾਂਦਾ ਹੈ। ਕਿਉਂ? forEach ਹਰ ਐਲੀਮੈਂਟ ਲਈ ਕਾਲਬੈਕ (callback) ਨੂੰ ਤੁਰੰਤ ਚਲਾਉਂਦਾ ਹੈ। ਇਹ ਹਰ ਇਟਰੇਸ਼ਨ (iteration) ਦੇ ਅੰਦਰਲੇ ਪ੍ਰੋਮਿਸ (promise) ਦੀ ਉਡੀਕ ਨਹੀਂ ਕਰਦਾ। async ਕੀਵਰਡ ਹਰ ਕਾਲਬੈਕ ਨੂੰ ਇੱਕ ਪ੍ਰੋਮਿਸ ਵਿੱਚ ਬਦਲ ਦਿੰਦਾ ਹੈ ਜਿਸ ਨੂੰ forEach ਤੁਰੰਤ ਨਜ਼ਰਅੰਦਾਜ਼ ਕਰ ਦਿੰਦਾ ਹੈ। ਤੁਹਾਡਾ ਲੂਪ ਮਾਈਕ੍ਰੋਸੈਕਿੰਡਾਂ ਵਿੱਚ ਖਤਮ ਹੋ ਜਾਂਦਾ ਹੈ; ਨੈੱਟਵਰਕ ਰਿਕੁਐਸਟਾਂ ਆਪਣੇ ਆਪ ਚੱਲਦੀਆਂ ਰਹਿੰਦੀਆਂ ਹਨ। ਜੇਕਰ ਤੁਹਾਨੂੰ ਗਲਤੀਆਂ (errors) ਨੂੰ ਕ੍ਰਮਵਾਰ ਸੰਭਾਲਣ ਦੀ ਲੋੜ ਹੈ, ਜਾਂ ਜੇਕਰ ਤੁਸੀਂ ਇਹ ਯਕੀਨੀ ਬਣਾਉਣਾ ਚਾਹੁੰਦੇ ਹੋ ਕਿ ਇੱਕ ਰਿਕੁਐਸਟ ਦੂਜੀ ਸ਼ੁਰੂ ਹੋਣ ਤੋਂ ਪਹਿਲਾਂ ਖਤਮ ਹੋ ਜਾਵੇ, ਤਾਂ ਇਹ ਪੈਟਰਨ ਦੋਵਾਂ ਗਾਰੰਟੀ ਨੂੰ ਚੁੱਪਚਾਪ ਤੋੜ ਦਿੰਦਾ ਹੈ।

ਇਸ ਨੂੰ for...of ਲੂਪ ਨਾਲ ਬਦਲੋ:

const urls = ['/api/user', '/api/posts', '/api/comments'];

for (const url of urls) {
  const res = await fetch(url);
  const data = await res.json();
  console.log(data);
}

console.log('All done!');

ਹੁਣ ਲੂਪ ਹਰ await 'ਤੇ ਅਸਲ ਵਿੱਚ ਰੁਕ ਜਾਂਦਾ ਹੈ। ਦੂਜੀ ਰਿਕੁਐਸਟ ਪਹਿਲੀ ਦੀ ਉਡੀਕ ਕਰਦੀ ਹੈ। 'All done!' ਸਭ ਕੁਝ ਸੈੱਟਲ ਹੋਣ ਤੋਂ ਬਾਅਦ ਹੀ ਪ੍ਰਿੰਟ ਹੁੰਦਾ ਹੈ।

for...of ਦੀ ਵਰਤੋਂ ਉਦੋਂ ਕਰੋ ਜਦੋਂ ਕ੍ਰਮ (sequence) ਮਹੱਤਵਪੂਰਨ ਹੋਵੇ—ਜਿਵੇਂ ਕਿ ਰੇਟ ਲਿਮਿਟਸ (rate limits) ਦਾ ਧਿਆਨ ਰੱਖਣ ਲਈ ਇੱਕ ਸਮੇਂ ਵਿੱਚ ਇੱਕ ਫਾਈਲ ਅਪਲੋਡ ਕਰਨਾ, ਇੱਕ ਖਾਸ ਕ੍ਰਮ ਵਿੱਚ ਡਾਟਾਬੇਸ ਰੋਅਜ਼ (database rows) ਲਿਖਣਾ, ਜਾਂ API ਕਾਲਜ਼ ਨੂੰ ਚੇਨ ਕਰਨਾ ਜਿੱਥੇ ਅਗਲੀ ਰਿਕੁਐਸਟ ਨੂੰ ਪਿਛਲੇ ਰਿਸਪਾਂਸ ਤੋਂ ਡਾਟਾ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ। ਜੇਕਰ ਤੁਸੀਂ ਅਸਲ ਵਿੱਚ ਪੈਰਲਲ ਐਗਜ਼ੀਕਿਊਸ਼ਨ (parallel execution) ਚਾਹੁੰਦੇ ਹੋ, ਤਾਂ forEach ਨਾਲ ਸਮਾਂ ਬਰਬਾਦ ਨਾ ਕਰੋ। ਸਿੱਧੇ ਤੌਰ 'ਤੇ Promise.all ਦੀ ਵਰਤੋਂ ਕਰੋ ਤਾਂ ਜੋ ਤੁਹਾਡਾ ਇਰਾਦਾ ਅਗਲੇ ਡਿਵੈਲਪਰ ਨੂੰ ਸਾਫ਼ ਦਿਖਾਈ ਦੇਵੇ। ਪਰ ਕਦੇ ਵੀ ਸਿੰਕਰੋਨਸ (synchronous) ਵਿਵਹਾਰ ਦੀ ਉਮੀਦ ਵਿੱਚ await ਅਤੇ forEach ਨੂੰ ਮਿਲਾਓ ਨਾ। ਅਜਿਹਾ ਨਹੀਂ ਹੋਵੇਗਾ।

Promise.allSettled ਦੀ ਵਰਤੋਂ ਉਦੋਂ ਕਰੋ ਜਦੋਂ 'ਜ਼ੀਰੋ' (zero) ਕੋਈ ਵਿਕਲਪ ਨਾ ਹੋਵੇ

Promise.all ਅਰਥਹੀਣ ਨਹੀਂ ਹੈ (semantically honest ਹੈ)। ਇਸ ਨੂੰ ਪ੍ਰੋਮਿਸਾਂ ਦਾ ਇੱਕ ਐਰੇ (array) ਦਿਓ, ਅਤੇ ਇਹ ਨਤੀਜਿਆਂ ਦਾ ਇੱਕ ਐਰੇ ਵਾਪਸ ਕਰਦਾ ਹੈ। ਪਰ ਮੁੱਖ ਗੱਲ ਇਹ ਹੈ ਕਿ ਜਿਵੇਂ ਹੀ ਕੋਈ ਇੱਕ ਪ੍ਰੋਮਿਸ ਰਿਜੈਕਟ (reject) ਹੁੰਦਾ ਹੈ, ਸਾਰਾ ਪ੍ਰੋਸੈਸ ਤੁਰੰਤ ਰਿਜੈਕਟ ਹੋ ਜਾਂਦਾ ਹੈ। ਬਾਕੀ ਸਾਰੇ ਪੈਂਡਿੰਗ ਪ੍ਰੋਮਿਸ ਆਪਣੇ ਆਪ ਖਤਮ ਹੋਣ ਲਈ ਛੱਡ ਦਿੱਤੇ ਜਾਂਦੇ ਹਨ, ਪਰ ਤੁਸੀਂ ਉਹਨਾਂ ਦੇ ਨਤੀਜਿਆਂ ਤੱਕ ਪਹੁੰਚ ਗੁਆ ਲੈਂਦੇ ਹੋ। ਪ੍ਰੋਡਕਸ਼ਨ (production) ਵਿੱਚ, ਇਹ 'ਸਭ ਕੁਝ ਜਾਂ ਕੁਝ ਵੀ ਨਹੀਂ' (all-or-nothing) ਵਾਲਾ ਵਿਵਹਾਰ ਨੁਕਸਾਨਦੇਹ ਹੁੰਦਾ ਹੈ।

ਕਲਪਨਾ ਕਰੋ ਕਿ ਤੁਹਾਡੀ ਐਪਲੀਕੇਸ਼ਨ ਚਾਰ ਸੁਤੰਤਰ ਸੇਵਾਵਾਂ (independent services) ਤੋਂ ਡੈਸ਼ਬੋਰਡ ਦੇ ਵਿਜੇਟਸ (widgets) ਫੈਚ ਕਰਦੀ ਹੈ: ਟ੍ਰੈਫਿਕ ਐਨਾਲਿਟਿਕਸ, ਰੈਵੇਨਿਊ ਡਾਟਾ, ਯੂਜ਼ਰ ਫੀਡਬੈਕ, ਅਤੇ ਸਰਵਰ ਹੈਲਥ। ਰੈਵੇਨਿਊ API ਵਿੱਚ ਥੋੜ੍ਹੀ ਦੇਰ ਲਈ ਟਾਈਮ-ਆਊਟ (timeout) ਹੋ ਜਾਂਦਾ ਹੈ। Promise.all ਦੇ ਅਧੀਨ, ਤੁਹਾਡਾ ਪੂਰਾ ਡੈਸ਼ਬੋਰਡ ਇੱਕ ਐਰਰ (error) ਦਿਖਾਉਂਦਾ ਹੈ। ਬਾਕੀ ਤਿੰਨ ਸਹੀ ਰਿਸਪਾਂਸ ਖ਼ਤਮ ਹੋ ਜਾਂਦੇ ਹਨ। ਯੂਜ਼ਰ ਨੂੰ ਇੱਕ ਸਪਿਨਰ (spinner) ਦਿਖਾਈ ਦਿੰਦਾ ਹੈ, ਅਤੇ ਫਿਰ ਫੇਲ੍ਹ ਹੋਣ ਵਾਲੀ ਸਕ੍ਰੀਨ, ਕਿਉਂਕਿ ਸਿਰਫ਼ ਇੱਕ ਚੌਥਾਈ ਡਾਟਾ ਵਿੱਚ ਸਮੱਸਿਆ ਸੀ।

Promise.allSettled ਤੁਹਾਨੂੰ ਇੱਕ ਵਧੇਰੇ ਸਿਆਣਾ ਵਿਕਲਪ ਦਿੰਦਾ ਹੈ। ਇਹ ਉਦੋਂ ਤੱਕ ਉਡੀਕ ਕਰਦਾ ਹੈ ਜਦੋਂ ਤੱਕ ਹਰ ਇੱਕ ਪ੍ਰੋਮਿਸ ਖਤਮ ਨਹੀਂ ਹੋ ਜਾਂਦਾ, ਚਾਹੇ ਉਹ ਸਫਲ ਹੋਵੇ ਜਾਂ ਫੇਲ੍ਹ। ਰੈਜ਼ੋਲਵਡ (resolved) ਮੁੱਲ ਹਰੇਕ ਨਤੀਜੇ ਦਾ ਵਰਣਨ ਕਰਨ ਵਾਲੇ ਆਬਜੈਕਟਾਂ ਦਾ ਇੱਕ ਐਰੇ ਹੁੰਦਾ ਹੈ:

const requests = [
  fetch('/api/traffic'),
  fetch('/api/revenue'),
  fetch('/api/feedback'),
  fetch('/api/health')
];

const results = await Promise.allSettled(requests);

results.forEach((result, index) => {
  if (result.status === 'fulfilled') {
    renderWidget(index, result.value);
  } else {
    renderError(index, result.reason);
  }
});

ਕੋਈ ਵੀ ਰਿਸਪਾਂਸ ਰੱਦ ਨਹੀਂ ਹੁੰਦਾ। ਤੁਸੀਂ ਜੋ ਕਰ ਸਕਦੇ ਹੋ ਉਹ ਰੈਂਡਰ (render) ਕਰਦੇ ਹੋ ਅਤੇ ਅਸਫਲਤਾ ਨੂੰ ਵੱਖ ਕਰ ਦਿੰਦੇ ਹੋ। ਇਹ ਪੈਟਰਨ ਉਦੋਂ ਮਹੱਤਵਪੂਰਨ ਹੁੰਦਾ ਹੈ ਜਦੋਂ ਤੁਸੀਂ ਅਸੰਬੰਧਿਤ ਕਾਰਜਾਂ (unrelated operations) ਨਾਲ ਨਜਿੱਠ ਰਹੇ ਹੁੰਦੇ ਹੋ—ਜਿਵੇਂ ਕਿ ਬਲਕ ਨੋਟੀਫਿਕੇਸ਼ਨ, Third-party webhook ਡਿਸ

ਇਸ ਵਿੱਚ ਦੋ ਮੁੱਖ ਗੱਲਾਂ ਹਨ। ਪਹਿਲੀ, ਤੁਹਾਡੇ runtime ਜਾਂ bundler ਨੂੰ ES modules ਦਾ ਸਮਰਥਨ ਕਰਨਾ ਚਾਹੀਦਾ ਹੈ। Node.js ਵਿੱਚ, ਇਸਦਾ ਮਤਲਬ ਹੈ ਜਾਂ ਤਾਂ .mjs extensions ਦੀ ਵਰਤੋਂ ਕਰਨਾ ਜਾਂ ਆਪਣੇ package.json ਵਿੱਚ "type": "module" ਸੈੱਟ ਕਰਨਾ। ਦੂਜੀ ਗੱਲ, ਕਿਉਂਕਿ ਮੋਡਿਊਲ ਪੱਧਰ 'ਤੇ ਉਡੀਕ ਕਰਨ ਨਾਲ ਹਰ importer ਦੇਰੀ ਨਾਲ ਚੱਲਦਾ ਹੈ, ਇਸ ਲਈ awaited ਕੰਮ ਨੂੰ ਸੀਮਤ (focused) ਰੱਖੋ। ਅਕਸਰ import ਕੀਤੇ ਜਾਣ ਵਾਲੇ utility ਫਾਈਲ ਦੇ ਉੱਪਰ ਭਾਰੀ sequential fetches ਤੁਹਾਡੀ ਪੂਰੀ application ਦੇ cold start ਨੂੰ ਹੌਲੀ ਕਰ ਦੇਣਗੇ। top-level await ਨੂੰ ਸਿਰਫ਼ ਉਹਨਾਂ ਅਸਲ bootstrap ਕੰਮਾਂ ਲਈ ਰੱਖੋ ਜਿਨ੍ਹਾਂ 'ਤੇ ਹੋਰ ਮੋਡਿਊਲ ਸੱਚਮੁੱਚ ਨਿਰਭਰ ਕਰਦੇ ਹਨ।

ਜਦੋਂ ਤੁਸੀਂ ਇਹ ਪੈਟਰਨ ਅਪਣਾਉਂਦੇ ਹੋ ਤਾਂ ਅਸਲ ਵਿੱਚ ਕੀ ਬਦਲਦਾ ਹੈ

ਪਹਿਲਾ ਫਾਇਦਾ ਭਵਿੱਖਬਾਣੀਯੋਗਤਾ (Predictability) ਹੈ। ਜਦੋਂ ਤੁਸੀਂ for...of loop ਪੜ੍ਹਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਨੂੰ ਪਤਾ ਹੁੰਦਾ ਹੈ ਕਿ ਹੇਠਾਂ ਵਾਲਾ ਬਲਾਕ ਕਦੋਂ ਖਤਮ ਹੋਵੇਗਾ। ਪਿੱਛੇ ਚੱਲ ਰਹੇ ਕੋਈ ਅਣਜਾਣ (ghost) promises ਨਹੀਂ ਹੁੰਦੇ, ਅਤੇ ਨਾ ਹੀ ਕੋਈ foreach callbacks ਤੁਹਾਡੇ error handlers ਤੋਂ ਵੱਖ ਹੁੰਦੇ ਹਨ। ਤੁਹਾਡਾ control flow ਸਕ੍ਰੀਨ 'ਤੇ ਦਿਖਾਈ ਦੇ ਰਹੇ ਕੋਡ ਦੇ ਰੂਪ ਨਾਲ ਮੇਲ ਖਾਂਦਾ ਹੈ।

ਅਗਲਾ ਫਾਇਦਾ ਲਚਕੀਲਾਪਣ (Resilience) ਹੈ। Promise.allSettled ਤੁਹਾਨੂੰ ਹਰ ਬਾਹਰੀ ਸਿਸਟਮ ਦੇ ਸੰਪੂਰਨ ਰਹਿਣ ਦੀ ਉਮੀਦ ਕਰਨ ਦੀ ਬਜਾਏ ਅੰਸ਼ਕ ਅਸਫਲਤਾ (partial failure) ਬਾਰੇ ਸੋਚਣ ਲਈ ਮਜਬੂਰ ਕਰਦਾ ਹੈ। Production software ਬਾਈਨਰੀ ਨਹੀਂ ਹੁੰਦਾ। ਕੁਝ endpoints ਅਸਫਲ ਹੋ ਸਕਦੇ ਹਨ। ਕੁਝ file reads ਵਿੱਚ permission errors ਆ ਸਕਦੇ ਹਨ। ਖਿੰਡੀਆਂ ਹੋਈਆਂ ਅਸਫਲਤਾਵਾਂ ਦੀ ਅਸਲੀਅਤ ਲਈ ਡਿਜ਼ਾਈਨ ਕਰਨਾ ਤੁਹਾਡੀ application ਨੂੰ ਸਹੀ ਰੱਖਦਾ ਹੈ ਅਤੇ ਜਾਇਜ਼ ਡੇਟਾ ਨੂੰ ਨਜ਼ਰਅੰਦਾਜ਼ ਨਹੀਂ ਕਰਦਾ।

ਸਪੱਸ਼ਟਤਾ (Clarity) ਇਸ ਸਭ ਨੂੰ ਜੋੜਦੀ ਹੈ। for...of ਇੱਕ ਸਾਧਾਰਨ ਅੰਗਰੇਜ਼ੀ ਪ੍ਰਗਤੀ ਵਾਂਗ ਪੜ੍ਹਿਆ ਜਾਂਦਾ ਹੈ। allSettled ਆਪਣੇ ਨਾਮ ਵਿੱਚ ਹੀ ਆਪਣਾ ਮਕਸਦ ਦੱਸ ਦਿੰਦਾ ਹੈ। Top-level await ਰਹੱਸਮਈ IIFE wrappers ਨੂੰ ਹਟਾ ਦਿੰਦਾ ਹੈ ਤਾਂ ਜੋ ਤੁਹਾਡੀਆਂ entry files syntactic acrobatics ਦੀ ਬਜਾਏ business logic ਨਾਲ ਸ਼ੁਰੂ ਹੋ ਸਕਣ। ਅਗਲਾ ਇੰਜੀਨੀਅਰ ਜੋ ਇਸ ਫਾਈਲ ਨੂੰ ਦੇਖੇਗਾ—ਚਾਹੇ ਉਹ ਤੁਸੀਂ ਛੇ ਮਹੀਨਿਆਂ ਬਾਅਦ ਹੋਵੋ ਜਾਂ ਕੋਈ ਸਾਥੀ ਜੋ ਡੈੱਡਲਾਈਨ 'ਤੇ ਹੋਵੇ—ਉਹ ਤੁਹਾਡਾ ਧੰਨਵਾਦ ਕਰੇਗਾ।

ਇੱਕ ਅਸਲ ਸਿੱਖਿਆ

async/await ਨੂੰ ਇੱਕ ਅਜਿਹੇ ਗਲੋਬਲ ਫਿਕਸ ਵਜੋਂ ਨਾ ਦੇਖੋ ਜਿਸ ਨੂੰ ਤੁਸੀਂ ਮੌਜੂਦਾ ਕੋਡ ਉੱਤੇ ਬਿਖੇਰ ਦਿੰਦੇ ਹੋ। ਆਪਣੇ ਮੌਜੂਦਾ ਪ੍ਰੋਜੈਕਟਾਂ ਵਿੱਚ ਇਹਨਾਂ ਤਿੰਨ ਖਾਸ anti-patterns ਦੀ ਜਾਂਚ ਕਰੋ। forEach ਬਲਾਕਾਂ ਦੇ ਅੰਦਰ await ਲੱਭੋ ਅਤੇ ਉਹਨਾਂ ਨੂੰ for...of ਜਾਂ ਜਾਣਬੁੱਝ ਕੇ ਵਰਤੇ ਗਏ Promise.all ਨਾਲ ਬਦਲੋ। ਹਰ Promise.all ਦੀ ਸਮੀਖਿਆ ਕਰੋ ਜੋ ਬਾਹਰੀ ਸੇਵਾਵਾਂ ਨਾਲ ਗੱਲ ਕਰਦਾ ਹੈ ਅਤੇ ਪੁੱਛੋ ਕਿ ਕੀ ਇੱਕ ਅਸਫਲਤਾ ਸੱਚਮੁੱਚ ਪੂਰੇ ਕੰਮ ਨੂੰ ਬਰਬਾਦ ਕਰ ਦੇਣੀ ਚਾਹੀਦੀ ਹੈ; ਜੇਕਰ ਨਹੀਂ, ਤਾਂ Promise.allSettled 'ਤੇ ਜਾਓ ਅਤੇ ਮਿਸ਼ਰਤ ਨਤੀਜਿਆਂ ਨੂੰ ਸੰਭਾਲੋ। ਅੰਤ ਵਿੱਚ, ਆਪਣੇ ES module entry points ਵਿੱਚੋਂ async IIFEs ਨੂੰ ਹਟਾ ਦਿਓ ਅਤੇ top-level await ਨੂੰ ਆਪਣੀ bootstrap sequence ਨੂੰ ਸਿੱਧੇ ਤੌਰ 'ਤੇ ਸੰਭਾਲਣ ਦਿਓ। ਇਹ ਛੋਟੇ ਮਕੈਨੀਕਲ ਬਦਲਾਅ ਹਨ, ਪਰ ਇਕੱਠੇ ਮਿਲ ਕੇ ਇਹ ਨਾਜ਼ੁਕ asynchronous scripts ਨੂੰ ਅਜਿਹੇ ਕੋਡ ਵਿੱਚ ਬਦਲ ਦਿੰਦੇ ਹਨ ਜਿਸ 'ਤੇ ਤੁਸੀਂ ਅਸਲ ਵਿੱਚ ਭਰੋਸਾ ਕਰ ਸਕਦੇ ਹੋ।