JavaScript異步編程:async/await與promise的未決狀態
async/await是JavaScript中處理異步操作的優雅方式,但當Promise長期處于未決狀態(pending)時,其行為值得深入探討。本文將分析await在Promise未resolve或reject時的表現。
場景分析
考慮以下代碼:
async function yyy() { console.log('yyy調用xxx開始'); let res = await xxx(); console.log('yyy調用xxx結束', res); } async function xxx() { return new Promise(function(resolve, reject) { // 沒有resolve或reject調用 }); } yyy();
xxx函數返回一個永遠處于pending狀態的Promise。yyy函數中的await xxx()會發生什么?
環境差異:Node.JS與瀏覽器
await的行為受運行環境影響:
立即學習“Java免費學習筆記(深入)”;
Node.js: 如果Promise保持pending狀態,且沒有其他活躍的異步操作(如定時器、網絡請求),Node.js的事件循環會認為程序已完成并退出。 它不會無限期等待未決的Promise。
瀏覽器: 在瀏覽器環境中,await 通常 會一直等待Promise完成。然而,如果缺乏其他異步任務來驅動事件循環,瀏覽器引擎可能認為腳本已執行完畢,不會阻塞頁面渲染或交互。 但如果該腳本是同步執行的(例如,直接在<script>標簽中或通過控制臺執行),則await后的代碼將不會執行,直到Promise狀態改變。</script>
代碼驗證:模擬長期運行的Promise
為了更清晰地展現瀏覽器環境下的行為,修改xxx函數:
async function yyy() { console.log('yyy調用xxx開始'); let res = await xxx(); console.log('yyy調用xxx結束', res); } async function xxx() { return new Promise(function(resolve, reject) { let startTime = Date.now(); let intervalId = setInterval(() => { let elapsedTime = Date.now() - startTime; console.log('xxx running...', elapsedTime); if (elapsedTime > 5000) { // 5秒后resolve clearInterval(intervalId); resolve('xxx completed'); } }, 1000); }); } yyy();
這段代碼中,xxx函數中的Promise會在5秒后resolve。在此期間,yyy函數將暫停執行,直到Promise resolve,然后繼續執行后續代碼。 如果注釋掉resolve調用,則yyy函數將無限期阻塞(在瀏覽器環境下,除非瀏覽器本身強制終止腳本)。
結論
await在Promise未決狀態下的行為取決于運行環境和是否存在其他異步操作。Node.js傾向于不等待,而瀏覽器則通常等待,但不會完全阻塞瀏覽器。 為了避免無限期阻塞,確保你的Promise最終會resolve或reject,或者使用超時機制來處理潛在的長時間未決狀態。