JavaScript: JavaScript Promise.race()


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Giới thiệu về phương thức tĩnh Promise.race() của JavaScript

Phương thức tĩnh Promise.race() chấp nhận một danh sách các promise dưới dạng một đối tượng có thể lặp (iterable) và trả về một promise mới, promise này sẽ được hoàn thành (fulfill) hoặc bị từ chối (reject) ngay khi có một promise được hoàn thành hoặc bị từ chối, với giá trị hoặc lý do từ promise đó.

Cú pháp của phương thức Promise.race()

Promise.race(iterable)

Trong cú pháp này, iterable là một đối tượng có thể lặp, chứa danh sách các promise.

Tên của phương thức Promise.race() ngụ ý rằng tất cả các promise này sẽ thi đua với nhau, và chỉ có một promise chiến thắng, bất kể promise đó được hoàn thành hay bị từ chối.

Xem sơ đồ sau:

Trong sơ đồ này:

  • promise1 được hoàn thành với giá trị v1 tại thời điểm t1.
  • promise2 bị từ chối với lỗi tại thời điểm t2.

promise1 được hoàn thành sớm hơn promise2, nên promise1 thắng cuộc đua. Do đó, Promise.race([promise1, promise2]) trả về một promise mới được hoàn thành với giá trị v1 tại thời điểm t1.

Xem sơ đồ khác:

Trong sơ đồ này:

  • promise1 được hoàn thành với giá trị v1 tại thời điểm t2.
  • promise2 bị từ chối với lỗi tại thời điểm t1.

promise2 được hoàn thành sớm hơn promise1, nên promise2 thắng cuộc đua. Do đó, Promise.race([promise1, promise2]) trả về một promise mới bị từ chối với lỗi tại thời điểm t1.

Ví dụ về Promise.race() trong JavaScript

Hãy cùng xem một số ví dụ sử dụng phương thức tĩnh Promise.race().

1) Ví dụ đơn giản về Promise.race()

Ví dụ sau tạo ra hai promise: một promise được hoàn thành sau 1 giây và một promise khác được hoàn thành sau 2 giây. Vì promise đầu tiên hoàn thành nhanh hơn promise thứ hai, nên Promise.race() hoàn thành với giá trị từ promise đầu tiên:

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('Promise đầu tiên đã được hoàn thành');
        resolve(10);
    }, 1 * 1000);
});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('Promise thứ hai đã được hoàn thành');
        resolve(20);
    }, 2 * 1000);
});

Promise.race([p1, p2])
    .then(value => console.log(`Đã hoàn thành: ${value}`))
    .catch(reason => console.log(`Bị từ chối: ${reason}`));

Ouput

Promise đầu tiên đã được hoàn thành
Đã hoàn thành: 10
Promise thứ hai đã được hoàn thành

Ví dụ sau tạo ra hai promise. Promise đầu tiên được hoàn thành sau 1 giây, trong khi promise thứ hai bị từ chối sau 2 giây. Vì promise đầu tiên nhanh hơn promise thứ hai, nên promise được trả về sẽ hoàn thành với giá trị của promise đầu tiên:

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('Promise đầu tiên đã được hoàn thành');
        resolve(10);
    }, 1 * 1000);
});

const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('Promise thứ hai đã bị từ chối');
        reject(20);
    }, 2 * 1000);
});

Promise.race([p1, p2])
    .then(value => console.log(`Đã hoàn thành: ${value}`))
    .catch(reason => console.log(`Bị từ chối: ${reason}`));

Output

Promise đầu tiên đã được hoàn thành
Đã hoàn thành: 10
Promise thứ hai đã bị từ chối

Lưu ý rằng nếu promise thứ hai nhanh hơn promise đầu tiên, promise được trả về sẽ bị từ chối với lý do của promise thứ hai.

2) Ví dụ thực tế về Promise.race()

Giả sử bạn cần hiển thị một bộ tải (spinner) nếu quá trình tải dữ liệu từ máy chủ mất quá nhiều thời gian.

Để làm điều này, bạn có thể sử dụng phương thức tĩnh Promise.race(). Nếu xảy ra tình trạng quá hạn (timeout), bạn sẽ hiển thị bộ tải, nếu không bạn sẽ hiển thị thông báo.

Dưới đây là mã HTML minh họa:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>JavaScript Promise.race() Demo</title>
    <link href="css/promise-race.css" rel="stylesheet">
</head>

<body>
    <div id="container">
        <button id="btnGet">Lấy Thông Điệp</button>
        <div id="message"></div>
        <div id="loader"></div>
    </div>
    <script src="js/promise-race.js"></script>
</body>
</html

Để tạo bộ tải, chúng ta sử dụng tính năng animation của CSS. Xem file promise-race.css để biết thêm chi tiết. Về mặt kỹ thuật, nếu một phần tử có lớp .loader, nó sẽ hiển thị bộ tải.

Đầu tiên, định nghĩa một hàm mới để tải dữ liệu. Hàm này sử dụng setTimeout() để mô phỏng một hoạt động không đồng bộ:

const DATA_LOAD_TIME = 5000;

function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const message = 'Promise.race() Demo';
            resolve(message);
        }, DATA_LOAD_TIME);
    });
}

Thứ hai, phát triển một hàm để hiển thị nội dung:

function showContent(message) {
    document.querySelector('#message').textContent = message;
}

Hàm này cũng có thể được sử dụng để đặt thông điệp về trạng thái trống.

Thứ ba, định nghĩa hàm timeout() trả về một promise. Promise này sẽ bị từ chối khi một khoảng thời gian (TIMEOUT) đã trôi qua.

const TIMEOUT = 500;

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(), TIMEOUT);
    });
}

Thứ tư, phát triển một cặp hàm để hiển thị và ẩn bộ tải:

function showLoadingIndicator() {
    document.querySelector('#loader').className = 'loader';
}

function hideLoadingIndicator() {
    document.querySelector('#loader').className = '';
}

Thứ năm, gắn một trình lắng nghe sự kiện nhấp chuột vào nút "Lấy Thông Điệp". Bên trong trình xử lý sự kiện nhấp chuột, sử dụng phương thức tĩnh Promise.race():

// xử lý sự kiện nhấp chuột
const btn = document.querySelector('#btnGet');

btn.addEventListener('click', () => {
    // đặt lại giao diện người dùng nếu người dùng nhấp vào lần thứ 2, 3, ...
    reset();
    
    // hiển thị nội dung hoặc bộ tải
    Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);
});

Chúng ta truyền hai promise vào phương thức Promise.race():

Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);

Promise đầu tiên lấy dữ liệu từ máy chủ, hiển thị nội dung và ẩn bộ tải. Promise thứ hai đặt một thời gian chờ (timeout).

Nếu promise đầu tiên mất hơn 500 ms để hoàn thành, catch() sẽ được gọi để hiển thị bộ tải. Khi promise đầu tiên hoàn thành, nó sẽ ẩn bộ tải.

Cuối cùng, phát triển một hàm reset() để ẩn thông điệp và bộ tải nếu nút được nhấp lần thứ hai.

// đặt lại giao diện người dùng
function reset() {
    hideLoadingIndicator();
    showContent('');
}

Kết hợp tất cả lại với nhau:

// sau 0,5 giây, nếu getData() chưa được hoàn thành, thì hiển thị bộ tải
const TIMEOUT = 500;
const DATA_LOAD_TIME = 5000;

function getData() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const message = 'Promise.race() Demo';
            resolve(message);
        }, DATA_LOAD_TIME);
    });
}

function showContent(message) {
    document.querySelector('#message').textContent = message;
}

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => reject(), TIMEOUT);
    });
}

function showLoadingIndicator() {
    document.querySelector('#loader').className = 'loader';
}

function hideLoadingIndicator() {
    document.querySelector('#loader').className = '';
}

// xử lý sự kiện nhấp chuột
const btn = document.querySelector('#btnGet');

btn.addEventListener('click', () => {
    // đặt lại giao diện người dùng nếu người dùng nhấp vào lần thứ 2, 3, ...
    reset();
    
    // hiển thị nội dung hoặc bộ tải
    Promise.race([getData()
            .then(showContent)
            .then(hideLoadingIndicator), timeout()
        ])
        .catch(showLoadingIndicator);
});

// đặt lại giao diện người dùng
function reset() {
    hideLoadingIndicator();
    showContent('');
}

Kết luận

Phương thức tĩnh Promise.race() rất hữu ích trong các tình huống mà bạn cần xử lý các promise khác nhau và chỉ quan tâm đến kết quả của promise hoàn thành nhanh nhất.

» Tiếp: JavaScript Throw Exception
« Trước: Phương thức bind()
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!