Map, filter and reduce với async/await

Nội dung bài viết

Async functions là gì? Bạn có thể sử dụng Map, filter và reduce trong Async functions, nhưng có thể bạn mắc sai lầm hay vô tình nào đó trong những trường hợp thế này không? Không phải lúc nào async-await đều chạy như bạn mong muốn.


Async functions là gì?

Các hàm async có sẵn trong Node và được biểu thị bằng từ keyword async trong khai báo của funciton. Async luôn trả lại một promise, ngay cả khi bạn không đón nhận nó một cách rõ ràng.

Ngoài ra, từ khóa await chỉ có sẵn bên trong các chức năng async tại thời điểm này - nó không thể được sử dụng trong phạm vi global. Có nghĩa là await chỉ hoạt động khi có khai báo async nếu không chúng ta sẽ nhận được một error.

Notes: Ở những tính năng mới ES10 thì chúng ta đã không cần khai báo keyword async nếu muốn sử dụng await. Xem thêm "Top- level await"

Bây giờ chúng ta sẽ phân tích với cách hoạt động của những method Map, filter and reduce với async/await

Async map JavaScript

function asyncThing (value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), 100);
  });
}

async function main () {
  return [1,2,3,4].map(async (value) => {
    const v = await asyncThing(value);
    return v * 2;
  });
}

main()
  .then(v => console.log(v))
  .catch(err => console.error(err));

Khi dùng Async map JavaScript chúng ta có kết qủa thật bất ngờ:

[ Promise {  }, Promise {  }, Promise {  }, Promise {  } ]

Điều mong đợi của chúng ta sau khi run chức năng đó là [ 2, 4, 6, 8 ]. Nếu bạn muốn nhận được điều đó thì chúng ta sử dụng Promise.all

main()
  .then(v => Promise.all(v))
  .then(v => console.log(v))
  .catch(err => console.error(err));

Nếu viết lại như thế này thì có thể đơn giản hơn?

function main () {
  return Promise.all([1,2,3,4].map((value) => asyncThing(value)));
}

main()
  .then(values => values.map((value) => value * 2))
  .then(v => console.log(v))
  .catch(err => console.error(err));

Async filter JavaScript

function asyncThing (value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), 100);
  });
}

async function main () {
  return [1,2,3,4].filter(async (value) => {
    const v = await asyncThing(value);
    return v % 2 === 0;
  });
}

main()
  .then(v => console.log(v))
  .catch(err => console.error(err));

Đối với filter khi sử dụng async-await thì có vẻ sai một chút ở đây. Vì theo như cú pháp của "filter JavaScript" thì sẽ return về true và false. Điều đó, đồng nghĩa là giá trị chúng ta sẽ nhận lại là [ false, true, false, true ]. Nhưng kết quả là nhận được giá tri lúc đầu chưa filter há há há.

Chuyện gì xảy ra với filter khi sử dụng async-await

Khi bạn sử dụng await trong filter callback, thì callback luôn là một promise. Mà Promise luôn là truthy, cho nên mọi item đia qua filter đều pass. Có nghĩa là, nếu bạn viết await trong filter như đoạn code dưới đây:

// Everything passes the filter…
const filtered = array.filter(true);

Vậy sao fix được chuyện này. Chả còn cách nào ngoài get value từ Promise() rồi filter sau thôi =]].

function asyncThing (value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), 100);
  });
}
async function main () {
  
  const arrTem = await Promise.all(await asyncThing([1,2,3,4]));

  return arrTem.filter((value) => {
    return value % 2 === 0;
  });
}
main()
  .then(v => console.log(v))
  .catch(err => console.error(err));

Async Reduce JavaScript

function asyncThing (value) {
  return new Promise((resolve) => {
    setTimeout(() => resolve(value), 100);
  });
}

async function main () {
  return [1,2,3,4].reduce(async (acc, value) => {
    return await acc + await asyncThing(value);
  }, Promise.resolve(0));
}

main()
  .then(v => console.log(v))
  .catch(err => console.error(err));

Còn với Reduce JavaScript thì quá ngon rồi, không cần giải thích nữa.

Chúc mọi người có những phút giây đau đầu thật là hại não :v

Ref: blog.risingstack.com

Có thể bạn đã bị missing