iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Modern Web

30天一起搞懂Web觀念系列 第 8

[DAY8] JS 的同步與非同步是什麼?(2)

  • 分享至 

  • xImage
  •  

昨天我們提了JS在非同步的執行流程,學了Call Stack、Callback Queue、Event Loop在執行時扮演的角色

接下來我們要更聚焦在這些非同步的工作,看看它們在程式碼中會長什麼樣子,以及如何從早期的 callback hell 一路演化到現在的 async/await


什麼是Callback Hell ?

  • 處理多個連續的非同步操作,將一個個非同步操作以巢狀迴圈的方式包起來
setTimeout(() => {
  console.log('A'); // A:1秒後執行
  
  setTimeout(() => {
    console.log('B'); // B:在A完成後,再過1秒執行
    
    setTimeout(() => {
      console.log('C'); // C:在B完成後,再過1秒執行
      
      // 如果還有任務D、E、F...
      // 就會一直向右延伸下去
      
    }, 1000);
  }, 1000);
}, 1000);

// 這個形狀 > 就是「毀滅金字塔」
  • 缺點:
    • 極難閱讀、理解和維護
    • 很有可能中間一個錯誤,導致整個程式崩潰

如何解決callback hell

使用 Promise

  • Promise 是一種物件,代表一個「尚未完成但最終會完成」的非同步操作。它允許你用鏈式 (.then()) 的方式來組織程式碼
  • 優點
    • 把程式碼攤平變的比較好閱讀
    • 可以用單一的 .catch() 來集中處理錯誤
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));

wait(1000)
  .then(() => {
    console.log('A');
    return wait(1000); // 返回一個新的 Promise
  })
  .then(() => {
    console.log('B');
    return wait(1000);
  })
  .then(() => {
    console.log('C');
  })
  .catch(err => {
    // 任何一個環節出錯,都會被這裡捕捉到
    console.error('發生錯誤:', err);
  });

使用 Async/Await

(目前主流的最佳實踐)

  • async/await 是建立在 Promise 之上的「語法糖」(Syntactic Sugar),它讓你可以用看起來像同步程式碼的方式來編寫非同步程式碼
  • 優點:程式碼非常直觀
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));

async function runTasks() {
  try {
    await wait(1000);
    console.log('A');

    await wait(1000);
    console.log('B');

    await wait(1000);
    console.log('C');
  } catch (err) {
    // 同樣可以用 try...catch 來處理所有錯誤
    console.error('發生錯誤:', err);
  }
}

runTasks();

另外還有一些很常出現的Promise.all()Promise.race()

Promise.all()

Promise.all() 可以同時啟動多個 Promise,等全部完成後才進到then(),如果中途有一個失敗,就直接進catch()

看一下他的語法怎麼寫:

const wait = (ms, label) => 
  new Promise(resolve => setTimeout(() => {
    console.log(label);
    resolve(label);
  }, ms));

Promise.all([
  wait(1000, 'A'),
  wait(2000, 'B'),
  wait(1500, 'C')
]).then(results => {
  console.log('All Done:', results);
}).catch(err => {
  console.error('A Task Failed:', err);
});

Promise.race()

Promise.race() 也是同時啟動多個 Promise,但只取第一個完成或失敗的結果

看一下語法怎麼寫

Promise.race([
  wait(1000, 'A'),
  wait(2000, 'B'),
  wait(1500, 'C')
]).then(result => {
  console.log('The First One Done:', result);
}).catch(err => {
  console.error('The First One Failed:', err);
});

參考資料

https://ithelp.ithome.com.tw/articles/10209328

https://ithelp.ithome.com.tw/m/articles/10297362


上一篇
[DAY7] JS 的同步與非同步是什麼?(1)
下一篇
[DAY9] 網頁是怎麼儲存資料的?(1)
系列文
30天一起搞懂Web觀念30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言