【 JavaScript 】Ajax 小學堂

回呼地獄 (Callback Hell)

讓我們使用 AJAX 來模擬通過一系列關卡的過程,並展示回呼地獄(Callback hell)的情況。在這個例子中,我們將使用 jQuery 的 AJAX 函式來發送非同步請求,成功後呼叫回呼函式。

假設每個關卡需要獲取一些資源,我們將透過 AJAX 來模擬這個過程。以下是範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
function passStage1(callback) {
console.log("通過關卡 1...");
$.ajax({
url: "/passStage1",
method: "GET",
success: function() {
console.log("關卡 1 完成!");
callback();
},
error: function() {
console.error("通過關卡 1 時發生錯誤!");
}
});
}

function passStage2(callback) {
console.log("通過關卡 2...");
$.ajax({
url: "/passStage2",
method: "GET",
success: function() {
console.log("關卡 2 完成!");
callback();
},
error: function() {
console.error("通過關卡 2 時發生錯誤!");
}
});
}

function passStage3(callback) {
console.log("通過關卡 3...");
$.ajax({
url: "/passStage3",
method: "GET",
success: function() {
console.log("關卡 3 完成!");
callback();
},
error: function() {
console.error("通過關卡 3 時發生錯誤!");
}
});
}

passStage1(function() {
passStage2(function() {
passStage3(function() {
console.log("恭喜你!你通過了所有關卡並贏得了遊戲!");
});
});
});

在這個例子中,我們定義了三個函式 passStage1passStage2passStage3,每個函式代表一個關卡。當玩家通過一個關卡後,將執行下一個關卡的函式。使用 AJAX 發送請求,當請求成功後,執行回呼函式,通過巢狀的回呼函式來模擬玩家通過一系列關卡的過程。

這樣看起來「不夠地獄」,實務上可能寫成這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
console.log("通過關卡 1...");
$.ajax({
url: "/passStage1",
method: "GET",
success: function() {
console.log("關卡 1 完成!");
console.log("通過關卡 2...");
$.ajax({
url: "/passStage2",
method: "GET",
success: function() {
console.log("關卡 2 完成!");
console.log("通過關卡 3...");
$.ajax({
url: "/passStage3",
method: "GET",
success: function() {
console.log("關卡 3 完成!");
console.log("恭喜你!你通過了所有關卡並贏得了遊戲!");
},
error: function() {
console.error("通過關卡 3 時發生錯誤!");
}
});
},
error: function() {
console.error("通過關卡 2 時發生錯誤!");
}
});
},
error: function() {
console.error("通過關卡 1 時發生錯誤!");
}
});

這種巢狀的回呼函式導致了程式碼巢狀層級過深,形成回呼地獄,讓程式碼難以閱讀和維護。這會增加程式碼出錯的可能性,並讓程式碼難以擴展和修改。

當聊到回呼地獄 (Callback Hell) 的時候,很常看到這張圖,可以很貼切的說明上方的情況。

Promise

為了解決這個回呼函式的問題,Promise 誕生了!我們使用現在瀏覽器已經內建的 fetch 來說明 Promise 的使用方式。來看一個實際的例子來說明 fetchPromise 的關係以及如何使用它們。

假設我們想要從一個提供 JSON 資料的 API 獲取一些資訊。這個過程包含發出請求、接收響應並處理這個響應。我們將使用 fetch 函式來發出請求,然後使用 Promise 的方法來處理結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 向一個 API 發出請求來獲取使用者資訊
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
// 首先檢查請求是否成功,如果成功,我們將響應轉換為 JSON 格式
if (!response.ok) {
throw new Error('網路請求失敗');
}
return response.json(); // 返回一個新的 Promise 物件,這個物件解析了 JSON 資料
})
.then(data => {
// 處理轉換後的 JSON 資料
console.log(data);
})
.catch(error => {
// 處理任何在請求過程中發生的錯誤
console.error('請求失敗:', error);
});

在這個例子中:

  1. 我們使用 fetchhttps://jsonplaceholder.typicode.com/users/1 發出 GET 請求。
  2. fetch 返回一個 Promise 物件,這個物件最終會被解析成 HTTP 響應。
  3. 使用 .then() 方法來設定當 Promise 物件解析成功時的回調函式。如果 HTTP 響應表明請求成功(response.ok 為真),我們將響應體轉換為 JSON。response.json() 也返回一個 Promise 物件,因此我們可以在它後面再接一個 .then() 來處理 JSON 資料。
  4. 第二個 .then() 接收到從 JSON 轉換的資料,並將其輸出到控制台。
  5. 如果在任何一步驟發生錯誤(例如網路問題或 JSON 解析錯誤),.catch() 方法將捕獲這些錯誤,並允許我們處理它們。

這個例子展示了 fetchPromise 如何共同工作來處理非同步 HTTP 請求,以及如何使用 .then().catch() 方法來處理成功和失敗的情況。

fetch vs Axios

接下來我們來進行一個常見的比較 fetch vs Axios。我們分別使用 fetch 和 axios 來發送 GET 請求,並進行比較。假設我們需要從一個 API 獲取使用者列表,以下是使用 fetch 和 axios 的範例程式碼。

使用 fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log('Fetch Users:', data);
})
.catch(error => {
console.error('Fetch Error:', error);
});

使用 axios

1
2
3
4
5
6
7
axios.get('https://jsonplaceholder.typicode.com/users')
.then(response => {
console.log('Axios Users:', response.data);
})
.catch(error => {
console.error('Axios Error:', error);
});

比較

我們進一步比較 fetch 和 axios 在其他方面的差異。

特性 fetch axios
支援度 fetch 是 Web APIs 的一部分,支援度較好,可以在現代瀏覽器中直接使用。 axios 是一個基於 Promise 的 HTTP 客戶端,可以在瀏覽器和 Node.js 中使用,支援度也很好。
攔截器 不支援攔截器,需要手動處理每個請求和回應。 支援攔截器,可以在請求和回應被發送或接收之前進行一些處理,例如添加公共的請求 header、logger 等。
取消請求 不支援取消請求功能,一旦請求發送就無法取消。 支援取消請求功能,可以在需要時取消正在進行的請求,以節省資源和提高性能。
自動轉換 JSON 數據 需要手動調用. json() 方法將回應轉換為 JSON 格式。 自動將回應的 JSON 數據轉換為 JavaScript 物件,無需手動處理。
客製化配置 不支援設置全局的默認配置,每次請求需要手動設置相關選項。 支援設置全局的默認配置,例如設置基本的 URL、超時時間、請求頭等,方便統一管理和使用。

axios 提供了許多 fetch 所不具備的便利功能,例如攔截器、取消請求、自動轉換 JSON 數據等,這使得 axios 在實際應用中更加靈活和方便。

評論