首頁 > web前端 > js教程 > 主體

JavaScript 中的 Promise:理解、處理和掌握非同步程式碼

WBOY
發布: 2024-09-03 14:18:32
原創
699 人瀏覽過

Promises in JavaScript: Understanding, Handling, and Mastering Async Code

簡介

我曾經是一名 Java 開發人員,我記得第一次接觸 JavaScript 中的 Promise 時。儘管這個概念看起來很簡單,但我仍然無法完全理解 Promise 是如何運作的。當我開始在專案中使用它們並了解它們解決的案例時,情況發生了變化。然後靈光乍現的時刻到來了,一切都變得更加清晰了。隨著時間的推移,Promise 成為我工具帶上的寶貴武器。當我可以在工作中使用它們並解決函數之間的非同步處理時,這是一種奇怪的滿足感。

您可能首先在從 API 取得資料時遇到 Promise,這也是最常見的範例。最近,我接受了採訪,猜猜第一個問題是什麼「你能告訴我 Promise 和 Async Await 之間的區別嗎?」。我對此表示歡迎,因為我認為這是一個很好的起點,可以更好地了解申請人如何理解這些機制的運作方式。然而,他或她主要使用其他庫和框架。它讓我記下差異並描述處理非同步函數錯誤的良好實踐。

承諾是什麼

讓我們從最初的問題開始:「Promise 是什麼?」Promise 是我們還不知道的值的佔位符,但我們將透過非同步計算/函數得到它。如果承諾順利的話,我們就會得到結果。如果 Promise 進展不順利,那麼 Promise 將會傳回錯誤。

Promise 的基本範例

定義一個承諾

透過呼叫 Promise 的建構子並傳遞兩個回呼函數來定義 Promise:resolvereject.

const newPromise = new Promise((resolve, reject) => {
    resolve('Hello');
    // reject('Error');
});
登入後複製

當我們想要成功解析 Promise 時,我們呼叫解析函數。拒絕是在評估我們的邏輯過程中發生錯誤時拒絕承諾。

檢索 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 字呼叫非同步函數。

Basic example of 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.

What’s different

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);
    }
};
登入後複製

Practical use cases

Promises are a fundamental feature in JavaScript for handling asynchronous code. Here are the main ways it is used:

Fetching Data from APIs

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.

Handling file operations

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);
登入後複製

Promise based libraries

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.

Repository with examples

All the examples can be found at: https://github.com/PrincAm/promise-example

Summary

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中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!