顯示具有 Javascript 標籤的文章。 顯示所有文章
顯示具有 Javascript 標籤的文章。 顯示所有文章

2025年5月31日 星期六

Promise.then() vs async/await:該怎麼選擇?

在撰寫 JavaScript 的非同步程式時,我們通常有兩種主流方式來處理 Promise 的結果:

  • 使用 .then() 搭配 .catch()
  • 使用 async/await 搭配 try/catch

那麼問題來了:
當你要拿一個 Promise 的結果做後續處理時,該用哪一種方式?

這篇文章會帶你比較兩者的差異、優缺點,並給出實務上的建議。

🔁 基本寫法比較

使用 .then()

fetchData()
  .then(data => {
    console.log("資料取得成功:", data);
  })
  .catch(err => {
    console.error("發生錯誤:", err);
  });

使用 await

async function main() {
  try {
    const data = await fetchData();
    console.log("資料取得成功:", data);
  } catch (err) {
    console.error("發生錯誤:", err);
  }
}
main();

✅ 各自的優缺點

項目 .then() async/await
可讀性 多層 .then() 可能會變難讀 結構接近同步邏輯,可讀性高
錯誤處理 使用 .catch() 可使用 try/catch 處理錯誤
控制流程 條件判斷或迴圈處理不直觀 iffor 等控制流可自然搭配使用
使用場景 非 async 環境(如模組頂層)適用 必須在 async 函式中使用
串接多個任務 .then().then().then() 較直覺 多個 await 也清楚,但需搭配邏輯

🧠 多段傳遞差異

.then() 支援連鎖傳值

Promise.resolve(2)
  .then(x => x * 3)        // 傳遞 6
  .then(y => y + 1)        // 傳遞 7
  .then(z => console.log(z)); // 印出 7

await 要自己手動接收變數

async function run() {
  const x = await Promise.resolve(2);
  const y = x * 3;
  const z = y + 1;
  console.log(z); // 印出 7
}

⚠️ 注意:.catch() 後還能繼續 .then()

你可能會以為錯誤後整個鏈就中止了,其實不是:

doSomething()
  .catch(err => {
    console.warn("錯誤被攔截:", err);
    return "預設值";
  })
  .then(result => {
    console.log("後續繼續執行:", result); // 收到預設值
  });

.catch() 沒有拋出新的錯誤,Promise 鏈會繼續執行後面的 .then()

🎯 什麼情況用哪一個?

建議使用 async/await:

  • 寫長流程邏輯時(可讀性更高)
  • 有多個非同步步驟需依序處理
  • 錯誤處理邏輯較複雜

建議使用 .then():

  • 在模組頂層(不方便加 async
  • 想快速串接簡單處理流程
  • 喜歡函式式風格或鏈式操作

✅ 結論

無論使用 .then() 還是 async/await,它們本質上都在操作 Promise。

  • .then() 更像函數式管線
  • await 更像同步的控制流
  • .catch() 可以中斷 .then() 鏈,也可以處理錯誤後繼續執行
  • .finally() 可以寫多個,做收尾工作

建議平時盡量用 async/await 搭配 try/catch
除非你的環境或需求更適合 .then() 鏈式處理。

如果你想要更深入的示範(例如多個非同步任務併發、錯誤復原機制、top-level await 實務寫法),也歡迎留言或關注後續文章。

2017年8月1日 星期二

JavaScript 對於字串內含有 Unicode 編碼的處理方式

在 Web 應用程式上要做關鍵字搜尋時,用的是自製的搜尋元件,前端與後端都是自製的。一直以來都沒有什麼問題,而這次的新案子測試人員發現罕用字出現了問題。

輸入關鍵字,查詢出來顯示時還是正常的,點選需要的資料帶入 textbox 時,罕用字卻變成了 &#xxxxx; 這樣的型式 (xxxxx 是10進位數字)

嘗試了呼叫 unescape、decodeURI、decodeURIComponent 都無法處理

後來只好問 google,找到一些資料之後,寫成以下的 function,提供出來給大家參考:

//--------------------------------------------
//  將字串裡的 unicode 換成文字
//--------------------------------------------
function ConvertUnicode(str)
{
    // 先處理 \uXXXX 這種型式的
    str = unescape(str);
    // 然後再處理 &#XXXXX; 這種型式的
    var regexp = /&#[0-9]+;/g;
    var match = regexp.exec(str);
    while (match !== null)
    {
        var s = String.fromCharCode(match[0].replace(/[&#;]/g, ""));
        str = str.replace(new RegExp(match[0], "g"), s);
        match = regexp.exec(str);
    }
    return str;
}

2011年5月12日 星期四

XMLHttpRequest 的應用:灌垃圾資料給釣魚網站

今天有一個許久未聯絡的同學忽然間丟 MSN 水球給我,第一時間的直覺就是詐騙,要買遊戲儲值卡的。果不其然,聊不到兩句就開始問我有沒有空,可不可以幫他買卡等等,我用幾個爛藉口推掉之後,他留下一個超連結就離線了。

點擊超連結,果然出現一個很像 Windows Live 登入的網頁,做得十分粗糙,連 Title 都沒有改,還是 “Untitled Document”,唉~~要騙人帳號也搞像一點嘛!於是我就拿出了我之前寫的小程式,一個 html 檔案,填入了一些資料,開始向那個釣魚網站灌垃圾資料。

這隻程式很簡單,就是使用 XMLHttpRequest 對釣魚網站 POST 假資料,假資料當然是使用亂數產生,主要的部份是這樣子的:

// 產生一個 XMLHttpRequest 物件
function initRequest()
{
    var A = null;
    try
    {
        A = new ActiveXObject("Msxml2.XMLHTTP")
    } 
    catch(e) 
    {
        try
        {
            A = new ActiveXObject("Microsoft.XMLHTTP")
        } 
        catch(oc) 
        {
            A = null
        }
    }
    if (!A && typeof XMLHttpRequest != "undefined")
    {
        A = new XMLHttpRequest()
    }
    if (!A && window.createRequest) 
    {
        try 
        {
            A = window.createRequest();
        } 
        catch (e) 
        {
            A = null;
        }
    } 
    return A
}
// 取亂數帳號密碼,塞入釣魚網頁
function DoBurst()
{
    try
    {
        // 宣告變數
        username = "";
        password = "";
        // 取得輸入資料
        actionUrl = document.getElementById("actionUrl").value;
        usernameId = document.getElementById("usernameId").value;
        passwordId = document.getElementById("passwordId").value; 
        
        // 取亂數拼湊假的帳號跟密碼
        c = Math.floor(Math.random() * 8) + 5;
        for (n=0; n < c; n++)
        {
            username += charSet.charAt(Math.floor(Math.random() * charSet.length));
        }
        username += document.getElementById("usernameSuffix").value;
        
        c = Math.floor(Math.random() * 8) + 5;
        for (n=0; n < c; n++)
        {
            password += charSet.charAt(Math.floor(Math.random() * charSet.length));
        } 

        // submit
        url = actionUrl + "?" + usernameId + "=" + username + "&" + passwordId + "=" + password;

        if (typeof XmlHttp == "undefined")
        {
            XmlHttp = initRequest();
        }

        XmlHttp.open("POST", url, true);
        XmlHttp.onreadystatechange = ShowResult;
        XmlHttp.send("");

        // 計數器累加,並更新畫面
        i++;
        document.getElementById("sendTimes").innerText = i;
    }
    catch(e)
    {
        // 可做錯誤處理
    }
}

然後使用 interval 定時重複執行,就這樣簡單!