我曾經是一名 Java 開發人員,我記得第一次接觸 JavaScript 中的 Promise 時。儘管這個概念看起來很簡單,但我仍然無法完全理解 Promise 是如何運作的。當我開始在專案中使用它們並了解它們解決的案例時,情況發生了變化。然後靈光乍現的時刻到來了,一切都變得更加清晰了。隨著時間的推移,Promise 成為我工具帶上的寶貴武器。當我可以在工作中使用它們並解決函數之間的非同步處理時,這是一種奇怪的滿足感。
您可能首先在從 API 取得資料時遇到 Promise,這也是最常見的範例。最近,我接受了採訪,猜猜第一個問題是什麼「你能告訴我 Promise 和 Async Await 之間的區別嗎?」。我對此表示歡迎,因為我認為這是一個很好的起點,可以更好地了解申請人如何理解這些機制的運作方式。然而,他或她主要使用其他庫和框架。它讓我記下差異並描述處理非同步函數錯誤的良好實踐。
讓我們從最初的問題開始:「Promise 是什麼?」Promise 是我們還不知道的值的佔位符,但我們將透過非同步計算/函數得到它。如果承諾順利的話,我們就會得到結果。如果 Promise 進展不順利,那麼 Promise 將會傳回錯誤。
透過呼叫 Promise 的建構子並傳遞兩個回呼函數來定義 Promise:resolve 和 reject.
const newPromise = new Promise((resolve, reject) => { resolve('Hello'); // reject('Error'); });
當我們想要成功解析 Promise 時,我們呼叫解析函數。拒絕是在評估我們的邏輯過程中發生錯誤時拒絕承諾。
我們使用內建函數 then 來取得 Promise 的結果。它有兩個傳遞的回調,結果和錯誤。當函數resolve成功解析Promise時,將會呼叫結果。如果 Promise 沒有解決,則會呼叫第二個函數錯誤。該函數由拒絕或拋出的另一個錯誤觸發。
newPromise.then(result => { console.log(result); // Hello }, error => { console.log("There shouldn't be an error"); });
在我們的範例中,我們將得到結果 Hello,因為我們成功解決了 Promise。
當 Promise 被拒絕時,總是會呼叫它的第二個錯誤回呼。
const newPromise1 = new Promise((resolve, reject) => { reject('An error occurred in Promise1'); }); newPromise1.then( (result) => { console.log(result); // It is not invoked }, (error) => { console.log(error); // 'An error occurred in Promise1' } );
為了清晰起見,更推薦的方法是使用內建的 catch 方法。
const newPromise2 = new Promise((resolve, reject) => { reject('An error occurred in Promise2'); }); newPromise2 .then((result) => { console.log(result); // It is not invoked }) .catch((error) => { console.log(error); // 'An error occurred in Promise2' });
catch 方法是鍊式的,並提供了自己的錯誤回呼。當 Promise 被拒絕時它會被呼叫。
兩個版本都運作良好,但連結在我看來更具可讀性,並且在使用我們進一步介紹的其他內建方法時非常方便。
一個承諾的結果可能是另一個承諾。在這種情況下,我們可以連結任意數量的 then 函數。
getJSON('categories.json') .then(categories => { console.log('Fetched categories:', categories); return getJSON(categories[0].itemsUrl); }) .then(items => { console.log('Fetched items:', items); return getJSON(items[0].detailsUrl); }) .then(details => { console.log('Fetched details:', details); }) .catch(error => { console.error('An error has occurred:', error.message); });
在我們的範例中,它用於縮小搜尋結果範圍以獲取詳細資料。每個 then 函數也可以有其錯誤回呼。如果我們只關心捕獲呼叫鏈中的任何錯誤,那麼我們可以利用 catch 函數。如果任何 Promise 回傳錯誤,它將被評估。
有時我們想等待更獨立的 Promise 的結果,然後根據結果採取行動。如果我們不關心 Promise 的解析順序,我們可以使用內建函數 Promise.all。
Promise.all([ getJSON('categories.json'), getJSON('technology_items.json'), getJSON('science_items.json') ]) .then(results => { const categories = results[0]; const techItems = results[1]; const scienceItems = results[2]; console.log('Fetched categories:', categories); console.log('Fetched technology items:', techItems); console.log('Fetched science items:', scienceItems); // Fetch details of the first item in each category return Promise.all([ getJSON(techItems[0].detailsUrl), getJSON(scienceItems[0].detailsUrl) ]); }) .then(detailsResults => { const laptopDetails = detailsResults[0]; const physicsDetails = detailsResults[1]; console.log('Fetched laptop details:', laptopDetails); console.log('Fetched physics details:', physicsDetails); }) .catch(error => { console.error('An error has occurred:', error.message); });
Promise.all 接受 Promise 陣列並傳回結果陣列。如果 Promise 之一被拒絕,則 Promise.all 也會被拒絕。
另一個內建功能是 Promise.race。當您有多個非同步函數(Promise)並且您想要對它們進行競賽時,可以使用它。
Promise.race([ getJSON('technology_items.json'), getJSON('science_items.json') ]) .then(result => { console.log('First resolved data:', result); }) .catch(error => { console.error('An error has occurred:', error.message); });
Promise 的執行可能需要不同的時間,Promise.race 會評估陣列中第一個已解決或拒絕的 Promise。當我們不關心順序但我們想要最快的非同步呼叫的結果時使用它。
如您所見,編寫 Promise 需要大量樣板程式碼。幸運的是,我們有原生的 Async Await 功能,這使得使用 Promises 變得更加容易。我們用“async”這個詞來標記一個函數,並且透過它,我們說在程式碼中的某個地方我們將呼叫非同步函數,我們不應該等待它。然後使用await 字呼叫非同步函數。
const fetchData = async () => { try { // Fetch the categories const categories = await getJSON('categories.json'); console.log('Fetched categories:', categories); // Fetch items from the first category (Technology) const techItems = await getJSON(categories[0].itemsUrl); console.log('Fetched technology items:', techItems); // Fetch details of the first item in Technology (Laptops) const laptopDetails = await getJSON(techItems[0].detailsUrl); console.log('Fetched laptop details:', laptopDetails); } catch (error) { console.error('An error has occurred:', error.message); } }; fetchData();
Our fetchData is marked as async and it allows us to use await to handle asynchronous calls inside the function. We call more Promises and they will evaluated one after the other.
We use try...catch block if we want handle the errors. Rejected error is then caught in the catch block and we can act on it like logging the error.
They are both features of JavaScript handling with asynchronous code. The main difference is in the syntax when Promises use chaining with then and catch but async await syntax is more in synchronous way. It makes it easier to read. Error handling for async await is more straightforward when it leverages try...catch block. This is a question that you can easily get at the interview. During the answer, you can get deeper into the description of both and highlight those differences.
Promise features
Of course, you can use all the features with async await. For example Promise.all.
const fetchAllData = async () => { try { // Use await with Promise.all to fetch multiple JSON files in parallel const [techItems, scienceItems, laptopDetails] = await Promise.all([ getJSON('technology_items.json'), getJSON('science_items.json'), getJSON('laptops_details.json') ]); console.log('Fetched technology items:', techItems); console.log('Fetched science items:', scienceItems); console.log('Fetched laptop details:', laptopDetails); } catch (error) { console.error('An error occurred:', error.message); } };
Promises are a fundamental feature in JavaScript for handling asynchronous code. Here are the main ways it is used:
As was already shown in the examples above, this is one of the most used use cases for Promises and you work with it daily.
Reading and writing files asynchronously can be done using promises, especially by Node.js module fs.promises
import * as fs from 'fs/promises'; const writeFileAsync = async (filePath, content, options = {}) => { try { await fs.writeFile(filePath, content, options); console.log(`File successfully written to ${filePath}`); } catch (error) { console.error(`Error writing file to ${filePath}:`, error.message); } }; const filePath = 'output.txt'; const fileContent = 'Hello, this is some content to write to the file!'; const fileOptions = { encoding: 'utf8', flag: 'w' }; // Optional file write options writeFileAsync(filePath, fileContent, fileOptions);
Axios is library that you should be familiar with. Axios handles HTTP requests in client and is vastly used.
Express is a web framework for Node.js. It makes it easy to build web apps and APIs, and when you use promises with Express, your code stays clean and easy to manage.
All the examples can be found at: https://github.com/PrincAm/promise-example
Promises are a fundamental part of JavaScript, essential for handling asynchronous tasks in web development. Whether fetching data, working with files, or using popular libraries like Axios and Express, you’ll frequently use promises in your code.
In this article, we explored what Promises are, how to define and retrieve their results, and how to handle errors effectively. We also covered key features like chaining, Promise.all, and Promise.race. Finally, we introduced async await syntax, which offers a more straightforward way to work with promises.
Understanding these concepts is crucial for any JavaScript developer, as they are tools you’ll rely on daily.
If you haven’t tried it yet, I recommend writing a simple code snippet to fetch data from an API. You can start with a fun API to experiment with. Plus, all the examples and code snippets are available in this repository for you to explore.
以上是JavaScript 中的 Promise:理解、處理和掌握非同步程式碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!