【JavaScript】深入理解Promise:從基礎概念到進階用法、手寫promise

在這里插入圖片描述

🔥 個人主頁:空白詩

在這里插入圖片描述

文章目錄

    • 一、引言
    • 二、Promise概述
      • 1. Promise的定義
      • 2. Promise的用途
      • 3. Promise的三種狀態
      • 4. Promise的構造函數和基礎結構
      • 5. Promise的優點
      • 6. Promise的實例方法
      • 7. Promise的靜態方法
    • 三、Promise的基本用法
      • 1. 創建一個Promise
      • 2. `then`方法
      • 3. `catch`方法
      • 4. `finally`方法
      • 5. 鏈式調用
      • 6. 示例代碼
    • 四、Promise的進階用法
      • 1. `Promise.all`
      • 2. `Promise.race`
      • 3. `Promise.allSettled`
      • 4. `Promise.any`
      • 5. 使用示例
    • 五、如何手寫一個簡單的Promise
      • 1. 實現Promise的基本結構
      • 2. 實現`then`方法
      • 3. 實現`catch`方法
      • 4. 實現`finally`方法
      • 5. 完整的Promise實現
    • 六、Promise的實際應用示例
      • 1. 異步數據加載
      • 2. 多個異步任務的并行處理
      • 3. 順序執行多個異步任務
      • 4. 處理并發的異步任務
    • 七、總結

在這里插入圖片描述

一、引言

在現代Web開發中,異步編程是不可避免的。無論是發起網絡請求、讀取文件、定時操作,還是處理事件,異步操作都無處不在。而在眾多異步編程解決方案中,Promise因其簡潔易用、鏈式調用和更好的錯誤處理機制,成為了開發者們的首選。

Promise是一種用于處理異步操作的JavaScript對象,它代表了一個在未來某個時間點才會完成(或失敗)的操作及其結果。相比于傳統的回調函數,Promise提供了一種更加優雅和便捷的方式來處理異步任務,從而避免了“回調地獄”的問題。

本文將詳細講解Promise的概念、基本用法和進階用法,并最終手寫一個簡單的Promise實現。通過這篇文章,讀者將不僅能夠深入理解Promise的工作原理,還能學會如何在實際項目中有效地使用Promise處理異步操作。


二、Promise概述

1. Promise的定義

Promise是JavaScript中的一種對象,用于表示一個在未來某個時間點才會完成(或失敗)的異步操作及其結果。它提供了一種處理異步操作的統一接口,使代碼更加簡潔和易于理解。與傳統的回調函數相比,Promise能夠更好地處理異步操作的結果和錯誤。

2. Promise的用途

Promise廣泛用于處理各種異步操作,特別是在以下場景中:

  • 網絡請求:使用fetchXMLHttpRequest進行Ajax調用。
  • 文件操作:讀取文件、寫入文件等I/O操作。
  • 定時操作:使用setTimeoutsetInterval進行延時操作。
  • 事件處理:處理用戶事件,如點擊、輸入等。
  • 數據庫操作:執行數據庫查詢、插入、更新等操作。

通過使用Promise,開發者可以避免嵌套的回調函數(即“回調地獄”),從而使代碼更加線性和易讀。

3. Promise的三種狀態

Promise有三種狀態:

  1. Pending(待定):初始狀態,操作尚未完成。
  2. Fulfilled(已完成):操作成功完成,并有一個結果值。
  3. Rejected(已拒絕):操作失敗,并有一個失敗原因。

一個Promise對象只能從Pending狀態轉變為Fulfilled狀態或Rejected狀態,并且狀態一旦改變,就不能再改變。這種不可變性保證了Promise的可靠性和可預測性。

4. Promise的構造函數和基礎結構

創建一個Promise實例需要傳遞一個執行函數,該函數接收兩個參數:resolve和reject。這兩個參數分別是函數,用于將Promise的狀態從Pending變為Fulfilled或Rejected。

const promise = new Promise((resolve, reject) => {// 異步操作if (操作成功) {resolve(成功的結果);} else {reject(失敗的原因);}
});

在這個基礎結構中,resolvereject函數分別用于處理操作成功和失敗的情況。當異步操作完成時,調用resolve將Promise的狀態變為Fulfilled,調用reject將狀態變為Rejected。

5. Promise的優點

Promise具有以下幾個顯著的優點:

  • 鏈式調用:可以通過鏈式調用then方法來處理異步操作的結果,使代碼更加簡潔。
  • 錯誤處理:通過catch方法統一處理錯誤,避免了多個回調函數中重復的錯誤處理代碼。
  • 狀態管理:Promise的狀態一旦改變,就不能再變,這種不可變性保證了異步操作結果的可靠性。
  • 組合操作:Promise提供了多種組合方法,如Promise.allPromise.racePromise.allSettledPromise.any,使得處理多個異步操作變得更加方便。

6. Promise的實例方法

  • then方法:用于在Promise狀態變為Fulfilled時,執行指定的回調函數,并返回一個新的Promise實例。
  • catch方法:用于在Promise狀態變為Rejected時,執行指定的回調函數,并返回一個新的Promise實例。
  • finally方法:用于在Promise狀態變為FulfilledRejected時,執行指定的回調函數,并返回一個新的Promise實例。
promise.then(result => {// 處理成功結果}).catch(error => {// 處理錯誤}).finally(() => {// 無論成功還是失敗,都會執行的操作});

通過這些實例方法,開發者可以靈活地處理異步操作的結果和錯誤,并執行最終的清理操作。

7. Promise的靜態方法

方法名描述示例代碼
Promise.resolve返回一個狀態為Fulfilled的Promise實例。Promise.resolve(42).then(value => console.log(value));
Promise.reject返回一個狀態為Rejected的Promise實例。Promise.reject(new Error('Error')).catch(error => console.error(error));
Promise.all接受一個Promise數組,返回一個新的Promise實例。當所有Promise都變為Fulfilled時,狀態變為Fulfilled;如果有任何一個Promise變為Rejected,狀態變為RejectedPromise.all([promise1, promise2]).then(values => console.log(values)).catch(error => console.error(error));
Promise.race接受一個Promise數組,返回一個新的Promise實例。當第一個Promise變為FulfilledRejected時,狀態立即變為FulfilledRejectedPromise.race([promise1, promise2]).then(value => console.log(value)).catch(error => console.error(error));
Promise.allSettled接受一個Promise數組,返回一個新的Promise實例。當所有Promise都變為FulfilledRejected時,狀態變為FulfilledPromise.allSettled([promise1, promise2]).then(results => console.log(results));
Promise.any接受一個Promise數組,返回一個新的Promise實例。當任意一個Promise變為Fulfilled時,狀態變為Fulfilled;如果所有Promise都變為Rejected,狀態變為RejectedPromise.any([promise1, promise2]).then(value => console.log(value)).catch(error => console.error(error));

這些靜態方法為處理多個異步操作提供了強大的工具,使得開發者可以根據具體需求選擇合適的方法來組合Promise。

綜上所述,Promise提供了一種簡潔、高效、可維護的方式來處理異步操作,使得JavaScript的異步編程變得更加便捷和強大。


三、Promise的基本用法

1. 創建一個Promise

創建一個Promise實例時,需要傳遞一個執行函數,該函數接收兩個參數:resolvereject。這兩個參數分別是函數,用于將Promise的狀態從Pending變為Fulfilled或Rejected。

const promise = new Promise((resolve, reject) => {// 異步操作if (操作成功) {resolve(成功的結果);} else {reject(失敗的原因);}
});

2. then方法

then方法用于在Promise狀態變為Fulfilled時,執行指定的回調函數。它接收兩個參數:第一個是處理Fulfilled狀態的回調函數,第二個是處理Rejected狀態的回調函數(可選)。

promise.then(result => {console.log('操作成功:', result);},error => {console.error('操作失敗:', error);}
);

3. catch方法

catch方法用于在Promise狀態變為Rejected時,執行指定的回調函數。它是then方法的語法糖,專門用于處理Rejected狀態。

promise.then(result => {console.log('操作成功:', result);}).catch(error => {console.error('操作失敗:', error);});

4. finally方法

finally方法用于在Promise狀態變為Fulfilled或Rejected時,執行指定的回調函數。無論Promise的最終狀態如何,finally中的回調函數都會執行。

promise.then(result => {console.log('操作成功:', result);}).catch(error => {console.error('操作失敗:', error);}).finally(() => {console.log('操作結束');});

5. 鏈式調用

Promise支持鏈式調用,這意味著可以在一個then方法之后繼續調用另一個then方法。這使得處理多個異步操作變得更加方便。

promise.then(result => {console.log('第一次操作成功:', result);return 另一個Promise;}).then(result => {console.log('第二次操作成功:', result);}).catch(error => {console.error('操作失敗:', error);});

6. 示例代碼

以下是一個完整的示例代碼,展示了如何創建一個Promise并使用thencatchfinally方法來處理異步操作的結果和錯誤。

const asyncOperation = () => {return new Promise((resolve, reject) => {setTimeout(() => {const success = Math.random() > 0.5;if (success) {resolve('操作成功');} else {reject('操作失敗');}}, 1000);});
};asyncOperation().then(result => {console.log(result);}).catch(error => {console.error(error);}).finally(() => {console.log('操作結束');});

在這個示例中,asyncOperation函數返回一個Promise,它模擬了一個異步操作,并在1秒鐘后根據隨機數決定操作是成功還是失敗。thencatchfinally方法分別用于處理操作的成功結果、錯誤和最終的清理工作。


四、Promise的進階用法

1. Promise.all

Promise.all接受一個Promise數組,返回一個新的Promise實例。當所有Promise都變為Fulfilled時,新Promise的狀態變為Fulfilled,并且它的結果是一個包含所有Promise結果的數組;如果有任何一個Promise變為Rejected,新Promise的狀態變為Rejected,并且它的結果是第一個被拒絕的Promise的原因。

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {setTimeout(resolve, 100, 'foo');
});Promise.all([promise1, promise2, promise3]).then(values => {console.log(values); // [3, 42, "foo"]
}).catch(error => {console.error(error);
});

2. Promise.race

Promise.race接受一個Promise數組,返回一個新的Promise實例。當第一個Promise變為Fulfilled或Rejected時,新Promise的狀態立即變為Fulfilled或Rejected,其結果就是第一個完成的Promise的結果。

const promise1 = new Promise((resolve, reject) => {setTimeout(resolve, 500, 'one');
});const promise2 = new Promise((resolve, reject) => {setTimeout(resolve, 100, 'two');
});Promise.race([promise1, promise2]).then(value => {console.log(value); // "two"
}).catch(error => {console.error(error);
});

3. Promise.allSettled

Promise.allSettled接受一個Promise數組,返回一個新的Promise實例。當所有Promise都變為Fulfilled或Rejected時,新Promise的狀態變為Fulfilled,并且它的結果是一個對象數組,每個對象表示對應的Promise的狀態和結果或原因。

const promise1 = Promise.resolve('成功');
const promise2 = Promise.reject('失敗');Promise.allSettled([promise1, promise2]).then(results => {results.forEach((result) => {if (result.status === 'fulfilled') {console.log('成功:', result.value);} else {console.log('失敗:', result.reason);}});
});

4. Promise.any

Promise.any接受一個Promise數組,返回一個新的Promise實例。當任意一個Promise變為Fulfilled時,新Promise的狀態變為Fulfilled,并且它的結果就是第一個成功的Promise的結果;如果所有Promise都變為Rejected,新Promise的狀態變為Rejected,并且它的結果是一個包含所有被拒絕原因的AggregateError對象。

const promise1 = Promise.reject('失敗1');
const promise2 = Promise.reject('失敗2');
const promise3 = Promise.resolve('成功');Promise.any([promise1, promise2, promise3]).then(value => {console.log(value); // "成功"
}).catch(error => {console.error(error); // AggregateError: All promises were rejected
});

5. 使用示例

以下是一個綜合示例,展示了如何使用Promise.allPromise.racePromise.allSettledPromise.any處理多個異步操作。

const fetchData1 = new Promise((resolve, reject) => {setTimeout(resolve, 1000, '數據1');
});const fetchData2 = new Promise((resolve, reject) => {setTimeout(resolve, 2000, '數據2');
});const fetchData3 = new Promise((resolve, reject) => {setTimeout(reject, 1500, '數據3失敗');
});// 使用Promise.all
Promise.all([fetchData1, fetchData2, fetchData3]).then(values => {console.log('Promise.all結果:', values);}).catch(error => {console.error('Promise.all錯誤:', error);});// 使用Promise.race
Promise.race([fetchData1, fetchData2, fetchData3]).then(value => {console.log('Promise.race結果:', value);}).catch(error => {console.error('Promise.race錯誤:', error);});// 使用Promise.allSettled
Promise.allSettled([fetchData1, fetchData2, fetchData3]).then(results => {console.log('Promise.allSettled結果:', results);});// 使用Promise.any
Promise.any([fetchData1, fetchData2, fetchData3]).then(value => {console.log('Promise.any結果:', value);}).catch(error => {console.error('Promise.any錯誤:', error);});

這個示例展示了如何使用不同的Promise靜態方法來處理多個異步操作。fetchData1fetchData2fetchData3是三個模擬異步操作的Promise,通過不同的方法組合來展示它們的結果和錯誤處理。


五、如何手寫一個簡單的Promise

為了更深入理解Promise的工作原理,我們將手寫一個簡單的Promise實現,涵蓋Promise的基本功能,包括狀態管理、then方法、catch方法和finally方法。

1. 實現Promise的基本結構

首先,我們需要定義Promise的三種狀態:PendingFulfilledRejected。接下來,我們定義一個Promise類,并在構造函數中初始化狀態和結果。

class MyPromise {// 定義靜態常量,表示Promise的三種狀態static PENDING = 'pending'; // 待定態static FULFILLED = 'fulfilled'; // 已完成態static REJECTED = 'rejected'; // 已拒絕態constructor(executor) {this.state = MyPromise.PENDING; // 初始狀態為待定態this.result = null; // 初始結果值為nullthis.callbacks = []; // 初始回調數組為空// 定義一個resolve函數,用于將Promise的狀態改為已完成態const resolve = value => {if (this.state === MyPromise.PENDING) {this.state = MyPromise.FULFILLED;this.result = value;this.callbacks.forEach(callback => {callback.onFulfilled(value);});}};// 定義一個reject函數,用于將Promise的狀態改為已拒絕態const reject = reason => {if (this.state === MyPromise.PENDING) {this.state = MyPromise.REJECTED;this.result = reason;this.callbacks.forEach(callback => {callback.onRejected(reason);});}};// 嘗試執行executor函數,并傳入resolve和reject作為參數try {executor(resolve, reject);} catch (error) {// 如果executor執行過程中拋出錯誤,則調用reject函數reject(error);}}// ...then, catch, finally 方法待實現
}

這段代碼中,MyPromise類模擬了Promise的基本行為,包括創建Promise時傳入的executor函數,以及resolve和reject兩個函數用于改變Promise的狀態。同時,還包含了處理executor執行過程中可能拋出的錯誤。

2. 實現then方法

then方法用于在Promise狀態變為Fulfilled或Rejected時,執行指定的回調函數。我們需要處理同步和異步執行的情況,并返回一個新的Promise。

class MyPromise {// ...constructor 和其他代碼then(onFulfilled, onRejected) {// 創建一個新的Promise實例并返回return new MyPromise((resolve, reject) => {// 定義一個處理回調的函數const handleCallback = (callback, state, result) => {try {// 執行相應的回調函數,并獲取其返回值const value = callback(result);// 如果返回值是一個Promise實例,則等待其解決或拒絕if (value instanceof MyPromise) {value.then(resolve, reject);} else {// 如果返回值不是Promise,則直接解決新的Promiseresolve(value);}} catch (error) {// 如果執行回調函數時拋出錯誤,則拒絕新的Promisereject(error);}};// 根據當前Promise的狀態,執行相應的回調函數if (this.state === MyPromise.FULFILLED) {// 如果當前Promise已成功,則異步執行onFulfilled回調setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0);} else if (this.state === MyPromise.REJECTED) {// 如果當前Promise已失敗,則異步執行onRejected回調setTimeout(() => handleCallback(onRejected, this.state, this.result), 0);} else {// 如果當前Promise還處于待定狀態,則將回調函數保存到callbacks數組中this.callbacks.push({onFulfilled: () => setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0),onRejected: () => setTimeout(() => handleCallback(onRejected, this.state, this.result), 0)});}});}// ...catch, finally 方法待實現
}

在這個實現中,then方法接受兩個參數:onFulfilledonRejected,它們分別是Promise成功和失敗時的回調函數。then方法會返回一個新的Promise實例。

如果當前Promise已經成功或失敗,then方法會異步執行相應的回調函數,并根據回調函數的返回值來解決或拒絕返回的新的Promise。

如果當前Promise還處于待定狀態,then方法會將回調函數保存到callbacks數組中,等待Promise狀態改變時再執行。

使用setTimeout是為了確保回調函數的執行是異步的,這符合Promise的規范。

3. 實現catch方法

MyPromise類中,catch方法是一個用于處理Promise被拒絕(rejected)情況的便捷方法。它實際上是對then方法的簡化調用,只關注拒絕情況的回調函數。因此可以說:catch方法是then方法的語法糖,只需調用then方法并將onRejected作為參數傳遞。

class MyPromise {// ...constructor, then method, 和其他代碼catch(onRejected) {// 調用this.then方法,傳入null作為成功的回調函數(因為不關心成功情況),// 并傳入onRejected作為拒絕的回調函數。// 然后返回this.then方法的結果,它是一個新的Promise實例。return this.then(null, onRejected);}// ...finally方法待實現
}

在這個實現中,catch方法只接受一個參數onRejected,它是Promise被拒絕時的回調函數。catch方法會調用then方法,并傳入null作為第一個參數(表示不關心Promise成功的情況),然后傳入onRejected作為第二個參數。最后,catch方法會返回then方法的結果,即一個新的Promise實例。

這樣,當Promise被拒絕時,catch方法提供的onRejected回調函數會被調用,并且任何在catch方法中返回的Promise都會成為catch方法返回的Promise的結果。如果onRejected回調函數本身返回一個Promise,那么catch方法返回的Promise將會等待這個返回的Promise解決或拒絕。

4. 實現finally方法

MyPromise類中,finally方法是一個用于指定不論Promise最終是fulfilled還是rejected,都會執行的操作的方法。它返回一個Promise。

class MyPromise {// ...constructor, then method, catch method, 和其他代碼finally(onFinally) {// 調用this.then方法,傳入兩個回調函數:// 第一個回調函數處理fulfilled情況,調用onFinally()并執行完畢后返回value;// 第二個回調函數處理rejected情況,調用onFinally()并執行完畢后拋出reason。return this.then(value => {// 當Promise成功解決時執行onFinally(); // 執行finally中的回調函數return value; // 返回原始值,以便鏈式調用中的下一個then可以使用},reason => {// 當Promise被拒絕時執行onFinally(); // 執行finally中的回調函數throw reason; // 拋出拒絕的原因,以便鏈式調用中的下一個catch可以捕獲});}
}

在這個實現中,finally方法接受一個參數onFinally,它是一個沒有參數的函數,表示無論Promise最終狀態如何都需要執行的操作。finally方法通過調用then方法來實現,它傳入兩個回調函數:一個用于處理fulfilled情況,另一個用于處理rejected情況。在這兩個回調函數中,都執行了onFinally(),以確保無論Promise的狀態如何,onFinally都會被調用。然后,根據Promise的狀態,返回相應的值或拋出相應的錯誤。

這樣,使用finally方法可以讓我們在Promise鏈的末尾添加一些清理操作,比如關閉文件、釋放資源等,而不用擔心這些操作會因為Promise的狀態而改變。

5. 完整的Promise實現

以下是一個完整的Promise實現,包括狀態管理、thencatchfinally方法。

// 定義一個簡單的Promise類
class MyPromise {// 定義Promise的三種狀態常量static PENDING = 'pending'; // 等待狀態static FULFILLED = 'fulfilled'; // 成功狀態static REJECTED = 'rejected'; // 失敗狀態// 構造函數,executor是執行器函數,接收resolve和reject兩個參數constructor(executor) {this.state = MyPromise.PENDING; // 初始狀態為pendingthis.result = null; // 存儲Promise的結果值或原因this.callbacks = []; // 存儲成功和失敗的回調函數// 定義resolve函數,將Promise狀態變為成功const resolve = value => {if (this.state === MyPromise.PENDING) { // 只有在pending狀態時才能改變狀態this.state = MyPromise.FULFILLED; // 將狀態變為成功this.result = value; // 存儲成功的結果值this.callbacks.forEach(callback => { // 執行所有成功的回調callback.onFulfilled(value);});}};// 定義reject函數,將Promise狀態變為失敗const reject = reason => {if (this.state === MyPromise.PENDING) { // 只有在pending狀態時才能改變狀態this.state = MyPromise.REJECTED; // 將狀態變為失敗this.result = reason; // 存儲失敗的原因this.callbacks.forEach(callback => { // 執行所有失敗的回調callback.onRejected(reason);});}};// 執行傳入的executor函數,傳入resolve和reject函數,并捕獲異常try {executor(resolve, reject);} catch (error) {reject(error); // 如果executor執行過程中拋出異常,直接調用reject}}// then方法,接收成功和失敗的回調函數then(onFulfilled, onRejected) {// 返回一個新的Promise實例return new MyPromise((resolve, reject) => {// 處理回調函數的執行和狀態傳遞const handleCallback = (callback, state, result) => {try {const value = callback(result); // 執行回調函數獲取返回值if (value instanceof MyPromise) { // 如果返回值是Promise,等待其狀態變化value.then(resolve, reject);} else {resolve(value); // 否則直接將返回值作為新Promise的成功結果}} catch (error) {reject(error); // 捕獲異常并將異常作為新Promise的失敗結果}};if (this.state === MyPromise.FULFILLED) {// 如果當前Promise已經是fulfilled狀態,異步執行onFulfilled回調setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0);} else if (this.state === MyPromise.REJECTED) {// 如果當前Promise已經是rejected狀態,異步執行onRejected回調setTimeout(() => handleCallback(onRejected, this.state, this.result), 0);} else {// 如果當前Promise仍處于pending狀態,將回調函數存儲起來等待執行this.callbacks.push({onFulfilled: () => setTimeout(() => handleCallback(onFulfilled, this.state, this.result), 0),onRejected: () => setTimeout(() => handleCallback(onRejected, this.state, this.result), 0)});}});}// catch方法,用于捕獲Promise鏈中的錯誤,等同于then(null, onRejected)catch(onRejected) {return this.then(null, onRejected);}// finally方法,用于在Promise執行結束后無論結果如何都會執行的回調finally(onFinally) {return this.then(value => {onFinally(); // 執行finally回調return value; // 繼續傳遞成功的結果值},reason => {onFinally(); // 執行finally回調throw reason; // 繼續傳遞失敗的原因});}
}// 示例用法
const promise = new MyPromise((resolve, reject) => {setTimeout(() => {const random = Math.random();if (random < 0.5) {resolve('成功值');} else {reject('失敗原因');}}, 1000);
});promise.then(value => {console.log('成功:', value);return '處理后的值';},reason => {console.error('失敗:', reason);throw new Error('處理失敗');}
).then(value => {console.log('處理后的結果:', value);}
).catch(error => {console.error('捕獲到的錯誤:', error.message);}
).finally(() => {console.log('無論如何都會執行的操作');}
);

成功情況:

成功: 成功值
處理后的結果: 處理后的值
無論如何都會執行的操作

失敗情況:

失敗: 失敗原因
捕獲到的錯誤: callback is not a function
無論如何都會執行的操作

通過這種方式,我們實現了一個簡化版的Promise,涵蓋了Promise的核心功能。這個實現可以幫助我們更好地理解Promise的工作原理和異步編程的機制。


六、Promise的實際應用示例

1. 異步數據加載

Promise常用于處理異步數據加載,例如從服務器獲取數據并在頁面上展示:

// 定義一個函數,用于從服務器獲取數據
function fetchDataFromServer(url) {return new Promise((resolve, reject) => {// 使用fetch API發送請求fetch(url).then(response => {// 檢查網絡響應是否成功if (response.ok) {return response.json(); // 返回JSON數據}throw new Error('Network response was not ok.'); // 網絡請求錯誤處理}).then(data => resolve(data)) // 將獲取的數據傳遞給resolve.catch(error => reject(error)); // 捕獲異常并傳遞給reject});
}// 使用示例
fetchDataFromServer('https://api.example.com/data').then(data => {console.log('成功獲取數據:', data); // 打印成功獲取的數據// 在頁面上展示數據}).catch(error => {console.error('獲取數據失敗:', error); // 打印獲取數據失敗的錯誤信息// 處理錯誤情況});

這段代碼定義了一個函數fetchDataFromServer,該函數接收一個URL作為參數,使用fetch API異步從該URL獲取數據,并將獲取的數據解析為JSON格式。

如果數據成功獲取并解析,函數將通過Promise的resolve方法返回這些數據;如果在獲取數據或解析數據過程中發生錯誤,函數將通過Promise的reject方法返回錯誤信息。這樣,調用者可以使用.then().catch()方法來處理成功獲取數據和獲取數據失敗的情況。

2. 多個異步任務的并行處理

Promise.all方法用于處理多個異步任務,等待所有任務完成后進行統一處理:

// 定義一個函數,模擬從服務器獲取用戶數據
const fetchUsers = () => {return new Promise((resolve, reject) => {setTimeout(() => resolve(['Alice', 'Bob', 'Charlie']), 1000); // 模擬異步獲取用戶數據});
};// 定義一個函數,模擬從服務器獲取文章數據
const fetchPosts = () => {return new Promise((resolve, reject) => {setTimeout(() => resolve(['Post 1', 'Post 2', 'Post 3']), 1500); // 模擬異步獲取文章數據});
};// 使用Promise.all處理多個異步任務
Promise.all([fetchUsers(), fetchPosts()]).then(([users, posts]) => {console.log('所有數據加載完成'); // 所有數據加載完成console.log('用戶列表:', users); // 打印用戶列表console.log('文章列表:', posts); // 打印文章列表// 在頁面上展示用戶和文章}).catch(error => {console.error('數據加載失敗:', error); // 打印數據加載失敗的錯誤信息// 處理錯誤情況});

這段代碼定義了兩個函數fetchUsersfetchPosts,它們分別模擬從服務器異步獲取用戶數據和文章數據。這兩個函數都返回一個Promise對象,該對象在異步操作完成時解析。

然后,代碼使用Promise.all方法來并行執行fetchUsersfetchPosts這兩個異步任務。Promise.all接收一個Promise數組作為參數,并返回一個新的Promise對象。這個新的Promise對象會在所有傳入的Promise都成功解析后解析,解析值為一個數組,包含所有傳入Promise的解析值。

Promise.all.then回調中,代碼處理了所有異步任務成功完成的情況。它打印出一條消息表示所有數據已加載完成,并分別打印出用戶列表和文章列表。

如果任何一個異步任務失敗,Promise.all返回的Promise對象會立即拒絕,并將拒絕原因傳遞給.catch回調。在.catch回調中,代碼處理了數據加載失敗的情況,并打印出錯誤信息。

總的來說,這段代碼演示了如何使用Promise.all來并行處理多個異步任務,并在所有任務都完成后或任何一個任務失敗時進行相應的處理。

3. 順序執行多個異步任務

Promise可以鏈式調用,實現多個異步任務的順序執行,確保每個任務在上一個任務完成后再執行:

// 定義一個模擬執行任務1的函數
const performTask1 = () => {return new Promise((resolve, reject) => {setTimeout(() => {console.log('任務1完成'); // 打印任務1完成resolve('任務1結果'); // 將任務1的結果傳遞給resolve}, 1000); // 模擬異步執行任務1});
};// 定義一個模擬執行任務2的函數
const performTask2 = () => {return new Promise((resolve, reject) => {setTimeout(() => {console.log('任務2完成'); // 打印任務2完成resolve('任務2結果'); // 將任務2的結果傳遞給resolve}, 1500); // 模擬異步執行任務2});
};// 執行任務1,然后在任務1完成后執行任務2
performTask1().then(result => {console.log('任務1返回結果:', result); // 打印任務1返回的結果return performTask2(); // 返回執行任務2的Promise對象}).then(result => {console.log('任務2返回結果:', result); // 打印任務2返回的結果// 繼續執行后續任務}).catch(error => {console.error('任務執行出錯:', error); // 打印任務執行出錯的錯誤信息// 處理錯誤情況});

這段代碼定義了兩個函數performTask1performTask2,它們分別模擬異步執行任務1和任務2。這兩個函數都返回一個Promise對象,該對象在異步操作完成時解析,并傳遞任務的結果。

然后,代碼通過調用performTask1函數開始執行任務1。在任務1的Promise解析后,.then回調被調用,并接收任務1的結果作為參數。在這個回調中,代碼打印出任務1的結果,并返回performTask2函數的調用結果,這是一個新的Promise對象,代表任務2的執行。

當任務2的Promise解析后,第二個.then回調被調用,并接收任務2的結果作為參數。在這個回調中,代碼打印出任務2的結果,并可以繼續執行后續的任務。

如果任何一個任務失敗,即任何一個Promise被拒絕,.catch回調會被調用,并接收拒絕原因作為參數。在這個回調中,代碼可以處理錯誤情況,并打印出錯誤信息。

總的來說,這段代碼演示了如何使用Promise的鏈式調用來順序執行多個異步任務,并在每個任務完成后或任何一個任務失敗時進行相應的處理。這種模式在實際開發中非常有用,特別是當您需要按順序執行一系列異步操作,并且每個操作都依賴于前一個操作的結果時。

4. 處理并發的異步任務

Promise.race方法用于處理多個異步任務,只處理第一個完成的任務結果,忽略其余的任務:

// 定義一個模擬執行任務1的Promise對象
const task1 = new Promise((resolve, reject) => {setTimeout(() => resolve('任務1完成'), 1000); // 模擬異步執行任務1
});// 定義一個模擬執行任務2的Promise對象
const task2 = new Promise((resolve, reject) => {setTimeout(() => resolve('任務2完成'), 500); // 模擬異步執行任務2
});// 使用Promise.race處理并發的異步任務
Promise.race([task1, task2]).then(result => {console.log('第一個完成的任務結果:', result); // 打印第一個完成的任務結果// 處理第一個完成的任務結果}).catch(error => {console.error('任務執行出錯:', error); // 打印任務執行出錯的錯誤信息// 處理錯誤情況});

這段代碼定義了兩個Promise對象task1task2,它們分別模擬異步執行任務1和任務2。task1將在1000毫秒后解析,而task2將在500毫秒后解析。

然后,代碼使用Promise.race方法來處理這兩個并發的異步任務。由于task2的解析時間比task1短,因此Promise.race返回的Promise對象將在task2解析時解析。

Promise.race.then回調中,代碼處理了第一個完成的任務的結果。它打印出第一個完成的任務的結果,并可以在這里進行進一步的處理。

如果任何一個任務失敗,即任何一個Promise被拒絕,Promise.race返回的Promise對象也會被拒絕,并將拒絕原因傳遞給.catch回調。在這個回調中,代碼可以處理錯誤情況,并打印出錯誤信息。

總的來說,這段代碼演示了如何使用Promise.race來處理多個并發的異步任務,并只關注第一個完成的任務的結果。這種模式在實際開發中非常有用,特別是當您有多個異步數據源,并且只需要從最快響應的那個數據源獲取結果時。


七、總結

Promise作為JavaScript中處理異步操作的一種重要機制,極大地簡化了代碼編寫和異步流程控制。在本篇文章中,我們深入探討了Promise的核心概念、基本用法、進階技巧以及如何手寫一個簡單的Promise,并通過實際應用示例展示了Promise在開發中的強大功能。

  1. Promise概述:Promise是JavaScript異步編程的核心,提供了一種更清晰、更直觀的方式來處理異步操作。它具有三種狀態:PendingFulfilledRejected,并且一旦狀態改變,就不會再次改變。

  2. Promise的基本用法:通過Promise對象,我們可以使用then方法處理成功的異步結果,使用catch方法處理失敗的異步結果,并使用finally方法在Promise結束時執行一些清理操作。

  3. Promise的進階用法:Promise的鏈式調用和靜態方法(如Promise.allPromise.race等)讓我們能夠更加靈活地處理多個異步任務,提供了并行和順序執行異步任務的解決方案。

  4. 手寫一個簡單的Promise:通過手寫一個簡單的Promise實現,我們更加深入地理解了Promise的內部工作機制,包括狀態管理、回調隊列的處理以及異步任務的執行。

  5. Promise的實際應用:在實際開發中,Promise被廣泛應用于異步數據加載、并行處理多個異步任務、順序執行異步任務以及處理并發的異步任務等場景,提升了代碼的可讀性和維護性。

總的來說,掌握Promise對于現代JavaScript開發者來說至關重要。它不僅幫助我們解決了回調地獄問題,還讓代碼更加簡潔和易于理解。希望通過這篇文章,你對Promise有了更加全面和深入的理解,并能夠在實際開發中靈活運用它來編寫高質量的異步代碼。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/44183.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/44183.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/44183.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

The First項目報告:引領L2解決方案新紀元的模塊化協議AltLayer

在區塊鏈演進中&#xff0c;可擴展性與定制化成為開發者核心訴求。ZK Rollups與Optimistic Rollups雖顯著提升以太坊等區塊鏈性能&#xff0c;卻面臨訪問性、定制難、中心化風險及流動性分散等挑戰。AltLayer以Rollups-as-a-Service創新模式&#xff0c;賦予開發者直接管理roll…

python class

繼承 看下Python中繼承的語法&#xff1a; class DerivedClassName(BaseClassName):<statement-1>...<statement-N>復制代碼 如果基類定義在另一個模塊中的時候&#xff1a; class DerivedClassName(modname.BaseClassName):

【漏洞復現】飛企互聯-FE企業運營管理平臺——uploadAttachmentServlet——文件上傳

聲明&#xff1a;本文檔或演示材料僅供教育和教學目的使用&#xff0c;任何個人或組織使用本文檔中的信息進行非法活動&#xff0c;均與本文檔的作者或發布者無關。 文章目錄 漏洞描述漏洞復現測試工具 漏洞描述 飛企互聯-FE企業運營管理平臺是一個基于云計算、智能化、大數據…

Linux用戶和用戶組的創建和添加

創建用戶組 在Linux中創建用戶組&#xff0c;您可以使用groupadd命令&#xff0c;后跟用戶組名稱。例如&#xff0c;要創建一個名為newgroup的用戶組&#xff0c;您可以執行以下命令&#xff1a; sudo groupadd newgroup創建用戶 創建用戶&#xff0c;您可以使用useradd命令&am…

平安養老險山西分公司開展2024年“7.8全國保險公眾宣傳日”活動

為深入貫徹新發展理念和中央金融工作會議、中央經濟工作會議精神&#xff0c;推動保險行業形象持續向好&#xff0c;根據中保協《關于印發<2024年“7.8全國保險公眾宣傳日”活動方案>的通知》和山西省保險行業協會《山西保險業2024年“7.8全國保險公眾宣傳日”活動方案》…

在誤裝Windows server2019 后如何利用Windows.old恢復?

&#x1f3c6;本文收錄于《CSDN問答解惑》專欄&#xff0c;主要記錄項目實戰過程中的Bug之前因后果及提供真實有效的解決方案&#xff0c;希望能夠助你一臂之力&#xff0c;幫你早日登頂實現財富自由&#x1f680;&#xff1b;同時&#xff0c;歡迎大家關注&&收藏&…

DevExpress(WinForms WPF)中文教程 - 如何減小文檔文件大小?

DevExpress擁有.NET開發需要的所有平臺控件&#xff0c;包含600多個UI控件、報表平臺、DevExpress Dashboard eXpressApp 框架、適用于 Visual Studio的CodeRush等一系列輔助工具。屢獲大獎的軟件開發平臺DevExpress近期重要版本v24.1已正式發布&#xff0c;該版本擁有眾多新產…

PlugLink 與 AI 大模型:深入 COZE API 鏈接實踐(附源碼)

在這個技術日新月異的時代&#xff0c;AI 不再是遙不可及的概念&#xff0c;而是逐漸成為我們日常生活與工作中不可或缺的一部分。作為技術領域的探路者&#xff0c;我深感榮幸地向大家介紹 PlugLink —— 一個旨在簡化 AI 應用集成并促進跨領域協作的開源平臺&#xff0c;以及…

開源數字人項目Hallo

硬件條件&#xff1a; gpu最低12G 軟件&#xff1a; cuda需支持 Python選擇3.10吧&#xff0c;我的版本3.11 源碼&#xff1a; GitHub - fudan-generative-vision/hallo: Hallo: Hierarchical Audio-Driven Visual Synthesis for Portrait Image Animation models文件&…

閱讀筆記——《Fuzz4All: Universal Fuzzing with Large Language Models》

【參考文獻】Xia C S, Paltenghi M, Le Tian J, et al. Fuzz4all: Universal fuzzing with large language models[C]//Proceedings of the IEEE/ACM 46th International Conference on Software Engineering. 2024: 1-13.【注】本文僅為作者個人學習筆記&#xff0c;如有冒犯&…

android paddingStart paddingLeft 使用區別

在 Android 開發中&#xff0c;paddingStart 和 paddingLeft 都是用來設置視圖的內邊距&#xff0c;但它們有一些重要的區別&#xff0c;尤其是在處理國際化和不同的布局方向&#xff08;LTR 和 RTL&#xff09;時&#xff1a; paddingLeft: 設置視圖內容左側的內邊距。只在從左…

科研繪圖系列:python語言實驗線圖(line Chart)

介紹 兩組數據在不同時間點的差異檢驗結果線圖 導入包 import pandas as pd import seaborn as sns import matplotlib.pyplot as plt from scipy import stats輸入數據 ctr = [2, 2, 3, 3, 4, 5, 5, 6, 4, 8, 6, 6, 9, 11, 12, 12, 15, 16, 20, 25, 27] drug = [2, 3, …

python開發prometheus exporter--用于hadoop-yarn監控

首先寫python的exporter需要知道Prometheus提供4種類型Metrics 分別是&#xff1a;Counter, Gauge, Summary和Histogram * Counter可以增長&#xff0c;并且在程序重啟的時候會被重設為0&#xff0c;常被用于任務個數&#xff0c;總處理時間&#xff0c;錯誤個數等只增不減的指…

查看wsl 版本

要查看Windows Subsystem for Linux (WSL) 的版本&#xff0c;您通常需要查看WSL的版本號以及正在運行的Linux發行版的版本。以下是檢查這兩個版本的方法&#xff1a; 1. 查看WSL的版本&#xff08;WSL 1 或 WSL 2&#xff09; 要檢查您的計算機上是否啟用了WSL 2&#xff0c…

如何錄制屏幕視頻?4款軟件,輕松錄屏

在數字化飛速發展的時代&#xff0c;如何錄制屏幕視頻已經成為我們工作、學習和娛樂中不可省略的一個重要問題。無論是制作教學教程還是錄制游戲視頻等&#xff0c;屏幕視頻錄制都為我們提供了極大的便利。今天&#xff0c;就讓我們一起探索如何錄制屏幕視頻的精彩方式&#xf…

多數據源配置導致注解Transactional失效

多數據源配置時&#xff0c;向事務管理器中注入數據源時可以直接注入DynamicRoutingDataSource而不要注入ItemDataSource類型&#xff0c;否則可能會出現事務失效問題。 其他事務失效問題參考&#xff1a; 注解Transaction踩坑指南 注解Transaction失效場景

Windows環境+C#實現顯示接口測試

代碼如下&#xff1a; using Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design; using System.Data; using System.Diagnostics; using System.Drawing; using System.IO; …

大模型“聚會”:國內卷價格,國外卷能力

[ GPT-4o和谷歌Gemini不斷宣傳現階段AI能力大幅度提高&#xff0c;那么這兩家&#xff0c;誰的大模型能力更強呢&#xff1f;這篇文章里&#xff0c;作者就展開了多維度的測試和體驗&#xff0c;想了解的同學&#xff0c;可以來看一下。 在中美AI大模型的競爭上&#xff0c;正衍…

從數據倉庫到數據湖(上):數據湖導論

文章目錄 一、什么是數據湖&#xff1f;起源數據湖的特征 二、為什么要用數據湖&#xff1f;三、數據湖與數據倉庫的區別數據倉庫和數據湖的對比 四、數據湖本質數據存儲架構數據處理工具&#xff1a;三類第一類工具第二類工具第三類工具 小結 五、總結六、參考資料 一、什么是…

[運維平臺]泛微運維平臺

運維平臺點擊登錄沒有反應&#xff0c;是因為H2數據庫損壞&#xff0c;H2數據庫在服務器異常重啟&#xff0c;磁盤滿等情況下容易損壞&#xff0c;請按照下面的步驟操作Linux&#xff1a; 運維平臺升級包https://www.weaver.com.cn/cs/monitorDownload.html 1&#xff09;請停…