我曾经是一名 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中文网其他相关文章!