Thực sự mình thấy vẫn mơ hồ về asynchronous (async-await) trong Javascript
Chào tất cả mn, chúc các bạn một ngày mới tốt lành. Hôm qua m có đọc được bài viết này trên diễn đàn : https://viblo.asia/p/mot-vai-ly-do-asyncawait-danh-bay-promises-4dbZN1dyKYM, mình có để lại comment dưới bài viết nhưng chờ chưa thấy bạn ấy phản hồi lại nên mình tạo ra topic này để mn cùng thảo luận.
Đầu tiên mình xin trích dẫn, các ý tóm lược chính của bạn ấy ở đầu bài viết :
1. Async/await cũng giống như promise, là non-blocking.
2. Async/await làm cho code bất đồng bộ nhìn và chạy gần giống như code đồng bộ.
3. Async/await là cách mới để viết code bất đồng bộ. Các phương pháp làm việc với code bất đồng bộ trước đây là sử dụng callback và promise.
4. Async/await là khái niệm được xây dựng ở tầng trên promise. Do đó nó không thể sử dụng với callback thuần.
Mình đọc đến hai ý đầu cảm thấy thắc mắc quá, hình như nó đang thực sự khác với những gì trước đây mình đang hiểu về về asynchronous
cũng như async-await
trong Javascript.
Theo ý hiểu của mình thì:
- Javascript là ngôn ngữ có cơ chế xử lý theo kiểu bất đồng bộ ( để tận dụng tốt performance ), tức là khi gặp một đoạn code xử lý mất nhiều time, Javascript sẽ không chờ đợi cho đến khi tác vụ đó hoàn thành mà tiếp tục chạy các câu lệnh phía dưới. Một ví dụ hay gặp nhất là thao tác gọi API ở server để fetch dữ liệu về.
- Chính vì cơ chế xử lý đó mà đôi khi luồng chạy của chương trình không được đảm bảo, lấy ví dụ như việc xử lý data thì ta phải có data trả về từ API sau đó mới có thể làm tiếp được các công việc khác như biến đổi data, nối, ghép vv...
- Để giải quyết vấn đề trên Javascript cung cấp cho ta 03 cách : + Callback, Promise, asyn-await Khi sử dụng một trong ba cách trên thì JS buộc sẽ phải đổi sang hướng chạy đồng bộ tức là chờ đoạn code trên xử lý xong mới thực thi đoạn code phía dưới.
Đó là ý hiểu của mình về đồng bộ & bất đồng bộ trong Javascrip, và mình vẫn đang thắc mắc :
Async/await cũng giống như promise, là non-blocking
==> tại sao nó lại là non-blocking, phải là blocking mới đúng chứ ?Async/await làm cho code bất đồng bộ nhìn và chạy gần giống như code đồng bộ.
==> tại sao lại chỉ là nhìn và chạy gần giống như thôi ?
Có phải kiến thức của m hiểu trước đây là không thực sự chưa đúng ?, Mong các bạn giúp m sáng tỏ vấn đề với !
8 CÂU TRẢ LỜI
Callback, Promise cũng như Async/Await đều là cơ chế bất đồng bộ (non-blocking) cả bạn ạ. Định nghĩa chi tiết ở trang chủ của mozilla
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Mình thấy bạn đang hơi hiểu nhầm về đồng bộ và bất đồng bộ (blocking và non-blocking). Code đồng bộ ví dụ như Python hay C/C++ chẳng hạn, code sẽ chạy theo thứ tự từ trên xuống dưới, chờ dòng trên chạy xong mới đến dòng dưới. Như bạn get API chẳng hạn, nó sẽ chờ đến khi kết quả trả về thì mới thực thi tiếp đoạn code ở dưới. Còn Javascript với 3 cơ chế trên khi chạy những đoạn code bất đồng bộ (cần thời gian phản hồi như getAPI) thì sẽ ko đứng đó mà sẽ chạy tiếp các đoạn code ở dưới.
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: null,
};
}
componentDidMount() {
fetch('https://api.mydomain.com')
.then(response => response.json())
.then(data => this.setState({ data }));
console.log("1");
}
}
export default App;
Như đoạn code trên, dòng console.log sẽ được chạy trước, đến khi có kết quả trả về từ API thì các đoạn code trong then mới được thực thi (gọi lại). Nói dễ hiểu hơn là khi có kết quả API, JS sẽ kêu "ê, tau có kết quả rồi nè, xử lý dữ liệu đi nhé."
tại sao vậy b ơi, bản chất bình thường JS nó đa là một ngôn ngữ chạy bất đồng bộ sẵn rồi chứ đâu phải thêm 03 cách kia vào thì nó mới thành xử lý bất đồng bộ đâu ?
@wiliamfeng JS bình thường nếu ko chạy các tác vụ bất đồng bộ (như đọc, ghi file, gọi API,...) thì cũng chạy đồng bộ như các ngôn ngữ khác thôi bạn.
Ví dụ bạn chạy thuật toán sắp xếp nổi bọt bằng JS chẳng hạn
let array = [1, 5, 2, 3, 8, 7]
let n = array.length.
for (let i = 0; i < n ; i++) {
for (let k = 0; k < n - i - 1; k++) {
if (array[k] > array[k + 1]) {
let temp = array[k];
array[k] = array[k + 1];
array[k + 1] = temp;
}
}
}
Hoàn toàn y chang như các ngôn ngữ khác, hoàn toàn đồng bộ (blocking)
bạn ơi, b nói là: "JS bình thường nếu ko chạy các tác vụ bất đồng bộ (như đọc, ghi file, gọi API,...) thì cũng chạy đồng bộ như các ngôn ngữ khác thôi "
thế nếu trong chương trình có đoạn code liên quan đến việc: như đọc, ghi file, gọi API,... thì nó có mặc định chạy sang thành bất đồng bộ không hay vân là đồng bộ,
và nếu vẫn là đồng bộ khi đó để cho nó thành bất đồng bộ ta phải dùng thêm 03 cách kia có phải vậy không ?
@wiliamfeng Bạn thử đọc về cơ chế Event Loop xem có rõ hơn ko. https://www.youtube.com/watch?v=8aGhZQkoFbQ
@wiliamfeng thật ra đoạn code nào bất đồng bộ thì phải dùng 1 trong 3 cái kia mới chạy được bất đồng bộ. Ví dụ đọc. ghi File chẳng hạn, nếu muốn bạn có thể để JS nó chạy đồng bộ được. Bạn có thể tham khảo qua Ví dụ này
@conglt bạn khẳng định lại (chốt lại) điều này giúp m với được không, còn cụ thể thế nào, dựa trên khẳng định đó, m se tìm hiểu tiếp:
b nói là: "JS bình thường nếu ko chạy các tác vụ bất đồng bộ (như đọc, ghi file, gọi API,...) thì cũng chạy đồng bộ như các ngôn ngữ khác thôi "
thế nếu trong chương trình có đoạn code liên quan đến việc: như đọc, ghi file, gọi API,... thì nó có mặc định chạy sang thành bất đồng bộ không hay vân là đồng bộ,
và nếu vẫn là đồng bộ khi đó để cho nó thành bất đồng bộ ta phải dùng thêm 03 cách kia có phải vậy không ?
==> tóm lại là thằng JS mặc định cũng giống mấy thằng ngôn ngữ khác là chạy đồng bộ, tuy nhiên nó có thêm công cụ hỗ trợ để chuyển thành bất đồng bộ, và khi nào muốn sử dụng theo kiểu bất đồng bộ thì phải thêm mấy thằng công cụ đó vào phải không bạn ?
@wiliamfeng Đúng bạn. Bạn không thể chạy một đoạn code bất đồng bộ trong JS nếu ko dùng Callback, Promises hoặc Async/Await
@thanhnguyen tks bạn nhé :
Theo mình hiểu thì function gắn async sẽ chạy như non-blocking và nó trả về Promise. Và await thì phải nằm trong async để đảm bảo kết quả gọi tuần tự (cho trường hợp lấy dữ liệu trước dùng cho câu lệnh bất đồng bộ sau) nhưng bạn có thể kết hợp await với Promise.all để chạy không tuần tự nhưng kết quả trả về vẫn tường minh.
Nói chung tùy tình huống mà bạn sử dụng async/await, promise sao cho code nó tường minh
async function foo() {
const a = await requestA();
const b = await requestB();
}
async function foo() {
const queue = [];
queue.push(requestA());
queue.push(requestB());
const result = Promise.all(queue);
}
function foo() {
const result = [];
requestA()
.then(a => result.push(a))
.then(requestB)
.then(b => result.push(b))
}
Câu hỏi của bạn có vẻ cần 1 bài tổng quan hơn để trả lời. Bạn có thể tham khảo thêm bài mình viết ở đây xem sao nhé
https://viblo.asia/p/tu-javascript-thuan-den-rxjs-phan-1-m68Z0OJzKkG
Không phải code mất nhiều time thì chạy bất đồng bộ, mà là có 2 trường hợp:
- truy cập môi trường bên ngoài, là những thứ không tồn tại trong process hiện tại, là những hàm native (được cung cấp sẵn) như http request, đọc file, truy cập db ...
- chủ động sử dụng những hàm để thực hiện bất đồng bộ, là những hàm nào trên Mozilla developer có nhé. Không phải lúc nào bất đồng bộ cũng nhanh hơn, nếu là tác vụ xử lý trên process hiện tại thì bất động bộ chỉ đơn giản là để sau rồi chạy, vẫn cần từng đó thời gian, trường hợp này có thể dùng worker.
Bài viết này sẽ giúp bạn hiểu rõ hơn bản chất của JS https://stackoverflow.com/questions/2035645/when-is-javascript-synchronous
Bạn hiểu JS luôn là bất đồng bộ -> cái này là sai
JS luôn đồng bộ và đơn luồng. Nó chỉ bất đồng bộ khi nào nó có thể làm điều đó.
cám ơn b nha, cám ơn b đa đóng góp thêm ý kiến cho m
tức là khi gặp một đoạn code xử lý mất nhiều time, Javascript sẽ không chờ đợi cho đến khi tác vụ đó hoàn thành mà tiếp tục chạy các câu lệnh phía dưới.
bạn hiểu nhầm cái này rồi, js nó là lập trình kịch bản, hành động , có khả năng hoạt động 1 cách độc lập (bất đồng bộ), tức là trong cùng 1 kịch bản thì nó vẫn hoạt động đồng bộ nhé.
cái thao tác gọi API bạn thấy nó không dừng lại chờ là do bản bạn sử dụng ajax , fetch hay axios nó làm sẵn cho bạn cái bất đồng bộ rồi.
1. Async/await cũng giống như promise, là non-blocking
Có lẽ là do câu chữ nên có thể làm bạn hơi khó hiểu. Mình nghĩ ý tác giả viết thế là vì một async function
sẽ return một Promise
nên khi các hàm khác dùng nó thì sẽ giống như là dùng Promise
. Mình cho ví dụ:
const sayHello = async () => {
console.log('Hello')
}
// Example 1:
console.log(sayHello())
> Promise
// Example 2:
sayHello().then(() => {
console.log('Hi!')
})
Như vậy thì một async function
sẽ chính là một Promise
- tức nếu xét cả cái async function
thì nó vẫn là một function bất đồng bộ. Tuy nhiên nội dung bên trong async function
thì đúng như bạn đang nghĩ, qua từ khóa await
, nó sẽ khiến cho code bên trong async function
chạy từ trên xuống dưới như ý đồ bạn mong muốn. Tóm lại là chúng ta đang dùng bất đồng bộ để khử bất đồng bộ.
2. Async/await làm cho code bất đồng bộ nhìn và chạy gần giống như code đồng bộ.
Tác giả nói đúng đó bạn, bản chất thì cái code bên trong cái async function
nó vẫn là code bất đồng bộ. Tuy nhiên cơ chế xử lý khác hơn một chút do trong ES6 có thêm một khái niệm gọi chung là Job Queue - một tầng phía trên của Event Loop Queue. Hiểu ngắn gọn thì trên ES6 implement thêm một cái Queue nữa để làm cho code trở nên chạy đồng bộ khi dùng async/await
. Mình giải thích tới đây thôi bạn tìm hiểu thêm về Event Loop, Job Queue trên ES6 nhé.
Vì setTimeout là 1 tác vụ bất đồng bộ (như đọc, ghi file, gọi API,...). Nó là webAPI
@wiliamfeng , hàm setTimeout truyền vào một callback function bạn ơi
Vì hàm setTimeout là một hàm được tính là bất đồng bộ, code vẫn đọc từ trên xuống nhưng nó gặp hàm setTimeout thì đưa nó vào queue và thực hiện sau. Mình hiểu là vậy
Cám ơn các b: @kdg , @chungminhtu , @Plumpboy đã thêm comment cho bài viết của m. Mình sẽ tìm hiểu thêm nhé
Chạy bất đồng bộ là non-blocking chứ bạn
@truongtrang.it đúng rồi, ban đầu nó chạy
bất đồng bộ
thì lànon-blocking
nhưng khi dùngAsync/await
thì nó thành đồng bộ thì phải làblocking
chứ. Bạn có đọc hết bài viết về thắc mắc của m chưa ?