01/10/2018, 16:07

Vấn đề throw sau lệnh sử dụng await, chương trình vẫn chạy tiếp

Cho đoạn code như sau:

async function main() {
  acb(); await waitNextCycle(); console.log(2);
}
function waitNextCycle() {
  return Promise.resolve();
}
async function acb() {
  await waitNextCycle(); console.log(1); throw new Error(5);
}
process.on('uncaughtException', function (err) {
  console.log('error'); console.error(err); process.exit(-1);
});
process.on('unhandledRejection', (err) => {
  console.log('reject'); console.error(err); process.exit(-2);
});
main();

Khi chạy đến throw new Error(5); mình kì vọng nó sẽ ngay lập tức nhảy vào event unhandledRejection và tắt chương trình.
Nhưng không, chương trình vẫn chạy tiếp từ chỗ console.log(2);, sau khi chạy hết hàm main, rồi chạy đến hết file, event unhandledRejection mới được chạy??
Vậy có cách nào để mình throw một phát là dừng chương trình luôn được không? (Mình không muốn để process.exit ngay chỗ throw đâu)

Tên Gì Cũng Được viết 18:15 ngày 01/10/2018

Hàm main phải gọi await abc() chứ nhỉ

明玉 viết 18:19 ngày 01/10/2018

Chương trình của mình không thể await ở acb được, thật sự trong app của mình thì acb nó là một lệnh require.
Hiện tại thì cách lý giải duy nhất mình đoán được là một lần await thì nodejs đẩy một event vào event loop, những event do await tạo ra có độ ưu tiên rất cao, còn unhandledRejection thì lại là một event có độ ưu tiên thấp :’(

*grab popcorn* viết 18:09 ngày 01/10/2018

Ngọc thử async trc, await sau

async function main() {
  let a = acb(); // trả về Promise
  await a; // lấy kết quả sau
  await waitNextCycle();
  console.log(2);
}

Tham khảo: https://kipalog.com/posts/JS--async-await-don-gian
Mục 3.4

明玉 viết 18:08 ngày 01/10/2018

nếu làm thế thì app mình chạy lộn xộn mất, chỗ acb đó trong app mình là một lệnh require, mình không để await chỗ đó là có chủ ý, thật sự là mình rành xài async await lâu rồi.
File đưọc require chứa khoảng vài trăm lệnh gọi hàm đều y như hàm acb, và trong file đó, trăm lệnh call đều không nằm trong function nào khác. File được require thì mình muốn nó đơn giản không lồng hàm.
Lý do có hàng trăm lệnh: mình đang vận dụng generative programming.

Hung viết 18:19 ngày 01/10/2018

Theo mình đoán Promise chỉ là JavaScript thuần thôi, bằng chứng ban đầu nó là defer của jQuery, rồi AngularJS gọi nó là q. Sau này JavaScript mới cho Promise thành standard api của Node

Còn pendingTask trong Event Loop, mình nghĩ, là hàm console.log, gọi tắt là log.
“log” thực chất là async function gọi đến libuv để in argument của log ra stdout, thuộc task của hệ điều hành (OS task)

Có 3 loại task được thực hiện trong event loop, thứ tự từ trên xuống dưới:

  • task liên quan đến timer (setTimeout, setInterval)
  • OS tasks: HTTP request, console.log, console.error,…
  • long running task: các package sử dụng ThreadPool trong libuv, như package fs, crypto,…

Sau khi đi kiểm tra lại source code, khi gọi resolve() hay reject() trên Promise, 1 trong 2 function này đều phải gọi ít nhất 1 lần setTimeout(function () { }, 0), mà setTimeout() tạo 1 timer task trong event loop.

Tên Gì Cũng Được viết 18:17 ngày 01/10/2018

async function main() {
acb(); // 1
await waitNextCycle(); // 2
console.log(2); // 3
}

giải pháp là dùng Promise.all
cho lệnh 1 và 2 vô list Promise.all
khi đó độ ưu tiên như nhau, ai chạy ra lội thì vô catch của promise, trong catch throw thêm cái nữa là dc nhỉ

明玉 viết 18:08 ngày 01/10/2018

Thôi mọi người không cần giúp nữa, mình viết lại code luôn rồi, cách làm ban đầu của mình không phù hợp với async await của JS engine. Lý do là Unhandled Rejection event có độ ưu tiên rất thấp so với các event khác, thấp hơn cả event của setTimeout.

Bài liên quan
0