Hướng dẫn và ví dụ JavaScript Promise, Async Await
1. Promise là gì?
Trong lập trình máy tính bạn rất thường xuyên làm việc với các hàm (function), và khi gọi một hàm bạn gần như ngay lập tức nhận được kết quả trả về.
function sum(a, b) {
return a + b;
}
// Call function:
let result = sum(10, 5);
console.log(result);
Giả sử bạn tạo ra một hàm có tên downloadFile(url) để download một tập tin từ Internet.
Việc download một tập tin lớn có thể mất vài phút hoặc lâu hơn. Khi gọi hàm downloadFile(url) một cách đồng bộ (synchronously) nó sẽ đóng băng mọi thao tác của người dùng cho tới khi nó hoàn thành, như vậy trong khoảng thời gian tập tin đang được download người dùng không thể thao tác với ứng dụng.
Vì vậy bạn không thể trông đợi một hàm giống như sau:
// Call download:
var myfile = downloadFile("http://example.com/file.mp3");
Promise?
- Câu trả lời là hàm này nên trả về một Promise (Lời hứa) thay vì một tập tin.
Hãy tưởng tượng, cô bạn gái xinh đẹp của bạn nói với bạn rằng "Hãy cưới em!". Okey, tất nhiên rồi, nhưng bạn không thể làm điều đó ngay lập tức vì bạn cần thêm thời gian để chuẩn bị, nhưng cô ấy đang chờ câu trả lời của bạn, và tốt nhất bạn nên trao cho cô ấy một lời hứa, và lập tức cả hai đều vui vẻ.
Promise State?
Một Promise có 3 trạng thái:
Trạng thái | Mô tả |
Pending | Lời hứa đang trong quá trình thực hiện, bạn không thể biết nó sẽ thành công hay thất bại. Chẳng hạn, tập tin đang được download. |
Fulfilled | Nhiệm vụ của lời hứa đã hoàn thành. Chẳng hạn, tập tin đã được download thành công. |
Rejected | Nhiệm vụ của lời hứa bị thất bại. Chẳng hạn, có một lỗi gì đó xẩy ra trong quá trình download tập tin. |
Cú pháp tạo một đối tượng Promise:
new Promise (
function (resolve, reject) {
// Codes
}
);
Ví dụ:
// [ECMAScript 5 Syntax]
var isNetworkOK = true;
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
} else {
console.log("File download process failed!"); // ***
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
Hàm downloadFile(url) của bạn sẽ trả về một đối tượng Promise, giống như sau:
// [ECMAScript 5 Syntax]
var isNetworkOK = true;
// This function return a Promise
function downloadFile(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
} else {
console.log("File download process failed!"); // ***
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
2. Sử dụng Promise
Ở phần trên chúng ta đã có hàm downloadFile(url), hàm này trả về một đối tượng Promise, bây giờ chúng ta sẽ tìm hiểu xem nào thế nào để sử dụng hàm này.
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(function (fulfilled) {
// Get a File
// Output: {fileName: 'file.mp3', fileContent: '...', fileSize: '3 MB'}
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
OK, Đây là code đầy đủ của ví dụ này:
promise-example.js
// [ECMAScript 5 Syntax]
var isNetworkOK = true;
// This function return a Promise
function downloadFile(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
} else {
console.log("File download process failed!"); // ***
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(function (fulfilled) {
// Get a File
// Output: {fileName: 'file.mp3', fileContent: '...', fileSize: '3 MB'}
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
Nếu isNetworkOK = true, khi chạy ví dụ bạn nhận được kết quả:
Nếu isNetworkOK = false, khi chạy ví dụ bạn nhận được kết quả:
3. Chaining Promise
Chaining Promise (Chuỗi lời hứa) là một tập các Promise liên tiếp được xích lại với nhau.
Hãy tưởng tượng bạn cần thực hiện 2 thao tác bao gồm, gọi hàm downloadFile(url) để download một tập tin từ Internet, sau đó gọi hàm openFile(file) để mở tập tin vừa download được.
Việc download một tập tin từ Internet tiêu tốn một khoảng thời gian, hàm downloadFile(url) được thiết kế trả về cho bạn một lời hứa (Promise). Bạn chỉ có thể có tập tin để mở nếu việc download là thành công. Vì vậy bạn cũng nên thiết kế hàm openFile(file) trả về một lời hứa (Promise).
Viết hàm openFile(file) trả về một đối tượng Promise.
function openFile(file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
function (resolve, reject) {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen; // Return a Promise.
}
Thay vì tạo ra một đối tượng Promise thông qua toán tử new, bạn có thể sử dụng phương thức tĩnh Promise.resolve(value) hoặc Promise.reject(error), 2 phương thức này trả về 1 đối tượng Promise.
Bạn có thể viết lại hàm openFile(file) ở trên một cách ngắn gọn hơn:
// Shorter:
function openFile(file) {
console.log("Start opening file ..."); // ***
var message = "File " + file.fileName + " opened!"
// Create a Promise
var willFileOpen = Promise.resolve(message);
return willFileOpen;
}
Gọi hàm downloadFile(url) và openFile(file) theo phong cách Promise:
console.log("Start app.."); // ***
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(openFile) // Chain it!
.then(function (fulfilled) { // If successful fileOpen.
// Get a message after file opened!
// Output: File file.mp3 opened!
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
console.log("End app.."); // ***
Xem code đầy đủ của ví dụ:
chaining-promise-example.js
// [ECMAScript 5 Syntax]
var isNetworkOK = true;
// This function return a Promise
function downloadFile(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
} else {
console.log("File download process failed!"); // ***
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
function openFile(file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
function (resolve, reject) {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen; // Return a Promise.
}
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(openFile) // Chain it!
.then(function (fulfilled) { // If successful fileOpen.
// Get a message after file opened!
// Output: File file.mp3 opened!
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
Chạy ví dụ và nhận được kết quả:
4. Promise là không đồng bộ
Promise là không đồng bộ (Asynchronous). Điều đó có nghĩa là khi bạn gọi một hàm Promise (Hàm trả về 1 Promise) nó sẽ không đóng băng ứng dụng của bạn trong quá trình lời hứa đang được thực hiện.
Để làm rõ vấn đề này chúng ta sửa lại ví dụ ở trên một chút. Sử dụng hàm setTimeout() để mô phỏng việc download tập tin sẽ bị tốn một khoảng thời gian nào đó.
chaining-promise-example-2.js
// [ECMAScript 5 Syntax]
var isNetworkOK = true;
// This function return a Promise
function downloadFile(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
function (resolve, reject) {
if (isNetworkOK) {
setTimeout( function() {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
}, 5 * 1000); // 5 Seconds
} else {
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
function openFile(file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
function (resolve, reject) {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen; // Return a Promise.
}
console.log("Start app.."); // ***
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(openFile) // Chain it!
.then(function (fulfilled) { // If successful fileOpen.
// Get a message after file opened!
// Output: File file.mp3 opened!
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
console.log("End app.."); // ***
Kết quả mà bạn nhận được khi chạy ví dụ trên:
Hãy chú ý tới thứ tự các message được in ra trên màn hình Console:
Start app..
Start downloading file ...
End app..
Complete the download process!
Start opening file ...
File file.mp3 opened!
5. Promise trong ES6
ECMAScript-6 đưa vào cú pháp hàm mũi tên (Narrow Function), vì vậy bạn có thể viết lại ví dụ trên theo cú pháp ES6:
chaining-promise-es6-example.js
// [ECMAScript 6 Syntax]
var isNetworkOK = true;
// This function return a Promise
downloadFile = function(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
(resolve, reject) => {
if (isNetworkOK) {
setTimeout( function() {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
}, 5 * 1000); // 5 Seconds
} else {
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
openFile = function (file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
(resolve, reject) => {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen; // Return a Promise.
}
console.log("Start app.."); // ***
// Call downloadFile(..) function:
// Returns a Promise object:
var willIGetAFile = downloadFile("http://example.com/file.mp3");
willIGetAFile
.then(openFile) // Chain it!
.then(function (fulfilled) { // If successful fileOpen.
// Get a message after file opened!
// Output: File file.mp3 opened!
console.log(fulfilled);
})
.catch(function (error) {
// Network Error!
// Output: There is a problem with the network.
console.log(error.message);
});
console.log("End app.."); // ***
Xem thêm về hàm và hàm mũi tên trong ECMAScript:
6. ES7 - Async Await
ECMAScript-7 giới thiệu cú pháp async và await, chúng giúp cho việc sử dụng Promise dễ dàng hơn, và dễ hiểu hơn.
chaining-promise-es7-example.js
// [ECMAScript 7 Syntax]
var isNetworkOK = true;
// An Asynchronous function return a Promise
async function downloadFile(url) {
console.log("Start downloading file ..."); // ***
// A Promise
var willIGetAFile = new Promise (
(resolve, reject) => {
if (isNetworkOK) {
setTimeout( function() {
console.log("Complete the download process!"); // ***
var file = {
fileName: 'file.mp3',
fileContent: '...',
fileSize: '3 MB'
};
resolve(file); // fulfilled
}, 5 * 1000); // 5 Seconds
} else {
var error = new Error('There is a problem with the network.');
reject(error); // reject
}
}
);
return willIGetAFile; // Return a Promise.
}
// An Asynchronous function return a Promise
async function openFile(file) {
console.log("Start opening file ..."); // ***
var willFileOpen = new Promise(
(resolve, reject) => {
var message = "File " + file.fileName + " opened!"
resolve(message);
}
);
return willFileOpen; // Return a Promise.
}
// Main Function (Asynchronous function)
async function mainFunction() {
try {
console.log("Start app.."); // ***
// Call downloadFile(..) function with 'await' keyword:
// It returns a File (Not Promise)
var file = await downloadFile("http://example.com/file.mp3");
console.log(file);
// Call openFile(..) function with 'await' keyword:
// It returns a String (Not Promise)
var message = await openFile(file);
console.log(message);
console.log("End app.."); // ***
} catch(e) {
console.log(e.message);
}
}
// Call Main Function:
(async () => {
await mainFunction();
})();
Các hướng dẫn ECMAScript, Javascript
- Giới thiệu về Javascript và ECMAScript
- Bắt đầu nhanh với Javascript
- Hộp thoại Alert, Confirm, Prompt trong Javascript
- Bắt đầu nhanh với JavaScript
- Biến (Variable) trong JavaScript
- Các toán tử Bitwise
- Mảng (Array) trong JavaScript
- Vòng lặp trong JavaScript
- Hàm trong JavaScript
- Hướng dẫn và ví dụ JavaScript Number
- Hướng dẫn và ví dụ JavaScript Boolean
- Hướng dẫn và ví dụ JavaScript String
- Câu lệnh rẽ nhánh if/else trong JavaScript
- Câu lệnh rẽ nhánh switch trong JavaScript
- Hướng dẫn xử lý lỗi trong JavaScript
- Hướng dẫn và ví dụ JavaScript Date
- Hướng dẫn và ví dụ JavaScript Module
- Lịch sử phát triển của module trong JavaScript
- Hàm setTimeout và setInterval trong JavaScript
- Hướng dẫn và ví dụ Javascript Form Validation
- Hướng dẫn và ví dụ JavaScript Web Cookie
- Từ khóa void trong JavaScript
- Lớp và đối tượng trong JavaScript
- Kỹ thuật mô phỏng lớp và kế thừa trong JavaScript
- Thừa kế và đa hình trong JavaScript
- Tìm hiểu về Duck Typing trong JavaScript
- Hướng dẫn và ví dụ JavaScript Symbol
- Hướng dẫn JavaScript Set Collection
- Hướng dẫn JavaScript Map Collection
- Tìm hiểu về JavaScript Iterable và Iterator
- Hướng dẫn sử dụng biểu thức chính quy trong JavaScript
- Hướng dẫn và ví dụ JavaScript Promise, Async Await
- Hướng dẫn và ví dụ Javascript Window
- Hướng dẫn và ví dụ Javascript Console
- Hướng dẫn và ví dụ Javascript Screen
- Hướng dẫn và ví dụ Javascript Navigator
- Hướng dẫn và ví dụ Javascript Geolocation API
- Hướng dẫn và ví dụ Javascript Location
- Hướng dẫn và ví dụ Javascript History API
- Hướng dẫn và ví dụ Javascript Statusbar
- Hướng dẫn và ví dụ Javascript Locationbar
- Hướng dẫn và ví dụ Javascript Scrollbars
- Hướng dẫn và ví dụ Javascript Menubar
- Hướng dẫn xử lý JSON trong JavaScript
- Xử lý sự kiện (Event) trong Javascript
- Hướng dẫn và ví dụ Javascript MouseEvent
- Hướng dẫn và ví dụ Javascript WheelEvent
- Hướng dẫn và ví dụ Javascript KeyboardEvent
- Hướng dẫn và ví dụ Javascript FocusEvent
- Hướng dẫn và ví dụ Javascript InputEvent
- Hướng dẫn và ví dụ Javascript ChangeEvent
- Hướng dẫn và ví dụ Javascript DragEvent
- Hướng dẫn và ví dụ Javascript HashChangeEvent
- Hướng dẫn và ví dụ Javascript URL Encoding
- Hướng dẫn và ví dụ Javascript FileReader
- Hướng dẫn và ví dụ Javascript XMLHttpRequest
- Hướng dẫn và ví dụ Javascript Fetch API
- Phân tích XML trong Javascript với DOMParser
- Giới thiệu về Javascript HTML5 Canvas API
- Làm nổi bật Code với thư viện Javascript SyntaxHighlighter
- Polyfill là gì trong khoa học lập trình?
Show More