Hướng dẫn promise all in python - hứa tất cả trong python

Trong JavaScript không đồng bộ, thật dễ dàng để chạy các tác vụ song song và chờ tất cả chúng hoàn thành bằng cách sử dụng

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
9:

async function bar[i] {
  console.log['started', i];
  await delay[1000];
  console.log['finished', i];
}

async function foo[] {
    await Promise.all[[bar[1], bar[2]]];
}

// This works too:
async function my_all[promises] {
    for [let p of promises] await p;
}

async function foo[] {
    await my_all[[bar[1], bar[2], bar[3]]];
}

Tôi đã cố gắng viết lại cái sau trong Python:

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]

Nhưng nó thực hiện các nhiệm vụ của tôi một cách tuần tự.

Cách đơn giản nhất để chờ đợi nhiều người đang chờ là gì? Tại sao cách tiếp cận của tôi không hoạt động?

Python Promise All With Code Examples

We'll attempt to use programming in this lesson to solve the Python Promise All puzzle. This is demonstrated in the code below.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]

With many examples, we have shown how to resolve the Python Promise All problem.

Can I use await with promise all?

all[] with async-await. Example 1: In this example we will creating two promises inside two different functions [or methods] and in another function we will accessing them using Promise. all[] along with making that function as async and promise resulting fetching will be done along with the await keyword.20-Aug-2022

What are Python promises?

A promise is an object returned by an asynchronous function, which represents the current state of the operation.27-Sept-2022

How do you make a promise in Python?

Promise[resolver] The resolver function is passed two arguments: resolve should be called with a single argument. If it is called with a non-promise value then the promise is fulfilled with that value. If it is called with a promise [A] then the returned promise takes on the state of that new promise [A].

Does Python have async await?

In this article, we show how to use async/await keywords in Python. With asynchronous programming, we can execute tasks concurrently with the main program execution. The async and await keywords simplify asynchronous programming in Python. Python has asynchronous programming model built into the language.29-Jul-2022

Is promise all fast?

In contrast, Promise. all[] fails fast by default, meaning the whole promise is rejected in case a call fails. So if an error occurs while fetching the third Pokemon, execution stops and we receive an error message.

Why do we use promise all?

The Promise. all[] method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will fulfill when all of the input's promises have fulfilled, or if the input iterable contains no promises.

Is Promise all parallel?

Often Promise. all[] is thought of as running in parallel, but this isn't the case. Parallel means that you do many things at the same time on multiple threads. However, Javascript is single threaded with one call stack and one memory heap.11-Dec-2020

Why is async await better than promises?

Promise chains can become difficult to understand sometimes. Using Async/Await makes it easier to read and understand the flow of the program as compared to promise chains.18-Apr-2022

Are promises async?

A promise is used to handle the asynchronous result of an operation. JavaScript is designed to not wait for an asynchronous block of code to completely execute before other synchronous parts of the code can run. With Promises, we can defer the execution of a code block until an async request is completed.

Does await return a promise?

If a promise resolves normally, then await promise returns the result. But in the case of a rejection, it throws the error, just as if there were a throw statement at that line.06-Feb-2022

Chời, thời này ai xài Promise nữa. Chuẩn bây giờ là async/await. – Ai đó trên mạng
– Ai đó trên mạng

Hãy khoan bạn ơi, đừng vội nhảy lên chuyến tàu tốc hành async/await trong khi chưa rành Promise, kẻo lại xảy ra “va chạm khi dồn dịch”, gây nên hậu quả khôn lường, vì căn bản async/await vẫn dùng Promise ở bên dưới mà thôi.

Ehkoo sẽ điểm những khái niệm căn bản về Promise, đồng thời so sánh với async/await để xem khi nào thì nên xài hàng nào nhé.

Nhắc lại, Promise là gì?

Promise là một cơ chế trong JavaScript giúp bạn thực thi các tác vụ bất đồng bộ mà không rơi vào callback hell hay pyramid of doom, là tình trạng các hàm callback lồng vào nhau ở quá nhiều tầng. Các tác vụ bất đồng bộ có thể là gửi AJAX request, gọi hàm bên trong 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
0, 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
1 hoặc 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
2, hay thao tác với WebSocket hoặc Worker… Dưới đây là một callback hell điển hình.

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]

Ví dụ trên khi được viết lại bằng Promise sẽ là:

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]

Để tạo ra một promise object thì bạn dùng class Promise có sẵn trong trình duyệt như sau:

const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]

Trong đó, 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
3 là một hàm có hai tham số:

  • api.getUser['pikalong']
      .then[user => api.getPostsOfUser[user]]
      .then[posts => api.getCommentsOfPosts[posts]]
      .catch[err => { throw err }]
    4 là hàm sẽ được gọi khi promise hoàn thành
  • api.getUser['pikalong']
      .then[user => api.getPostsOfUser[user]]
      .then[posts => api.getCommentsOfPosts[posts]]
      .catch[err => { throw err }]
    5 là hàm sẽ được gọi khi có lỗi xảy ra

Ví dụ:

api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}

Như vậy 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
6 sẽ trả về một promise object. Chúng ta có thể truy xuất đến kết quả trả về bằng phương thức 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7 như sau:

function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]

Phương thức 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
8 nhận vào hai hàm: 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
9 được gọi khi promise hoàn thành và 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
0 được gọi khi có lỗi xảy ra. Bên trong tham số 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
9 bạn có thể trả về một giá trị đồng bộ, chẳng hạn như giá trị số, chuỗi, 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
2, 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
3, array hay object; hoặc một promise object khác. Các giá trị bất đồng bộ sẽ được bọc bên trong một Promise, cho phép bạn kết nối [chaining] nhiều promises lại với nhau.promise object khác. Các giá trị bất đồng bộ sẽ được bọc bên trong một Promise, cho phép bạn kết nối [chaining] nhiều promises lại với nhau.

promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]

Trong ví dụ trên, bạn thấy đến phương thức 

const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
4. Phương thức này chỉ là cú pháp bọc đường[syntactic sugar] của 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
5 mà thôi. Chúng ta sẽ nói thêm về 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
4 ở bên dưới.

Tạo nhanh Promise với 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
7 và 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
8

Có những trường hợp bạn chỉ cần bọc một giá trị vào promise hay tự động reject. Thay vì dùng cú pháp 

const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
9 dài dòng, bạn có thể dùng hai phương thức tĩnh 
api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
0 và 
api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
1

const p = Promise.resolve[12]
  .then[result => console.log[result]] // 12
  .then[res => Promise.reject[new Error['Dừng lại nhanh']]]
  .then[[] => 'Cười thêm phát nữa là tym anh đứt phanh']
  .catch[err => console.error[err]] // Error: Dừng lại nhanh

Còn async/await là cái chi?

Được giới thiệu trong ES8, async/await là một cơ chế giúp bạn thực hiện các thao tác bất đồng bộ một cách tuần tự hơn. Async/await vẫn sử dụng Promise ở bên dưới nhưng mã nguồn của bạn [theo một cách nào đó] sẽ trong sáng và dễ theo dõi.

Để sử dụng, bạn phải khai báo hàm với từ khóa 

api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
2. Khi đó bên trong hàm bạn có thể dùng 
api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
3

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
0

Cần lưu ý là kết quả trả về của async function luôn là một Promise.

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
1

Căn bản về Promise và async/await là vậy. Hiện giờ, bạn đã có thể sử dụng Promise và async/await ở tất cả các trình duyệt hiện đại [trừ IE11 ra nhé, bạn vẫn cần polyfill cho nó]. Hãy xem những trường hợp cần lưu ý khi sử dụng chúng.

“Kim tự tháp” Promises

Một lỗi chúng ta hay mắc phải khi mới làm quen với Promise, đó là tạo ra “kim tự tháp” promises như thế này.

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
2

Lý do vì chúng ta quên mất tính chất liên kết [chaining] của promise, cho phép bên trong hàm 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
4 có thể trả về một giá trị đồng bộ hoặc một promise khác. Do đó cách giải quyết là:một promise khác. Do đó cách giải quyết là:

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
3

Theo Ehkoo, việc hiểu và sử dụng thành thạo tính liên kết là một trong những điểm QUAN TRỌNG NHẤT khi làm việc với Promise. Khi promise lồng vào nhau từ 2 tầng trở lên thì đã đến lúc bạn phải refactor lại rồi.QUAN TRỌNG NHẤT khi làm việc với Promise. Khi promise lồng vào nhau từ 2 tầng trở lên thì đã đến lúc bạn phải refactor lại rồi.

Luôn đưa vào 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7 một hàm

Bạn thử đoán xem đoạn code sau sẽ in ra gì?

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
4

Câu trả lời là 

api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
6 đó. Phương thức 
api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
7 đòi hỏi tham số của nó phải là một hàm. Nếu bạn đưa vào 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7 một giá trị, nó sẽ bị bỏ qua, giải thích tại sao đoạn code trên hiển thị 
api.getUser = function[username] {
  // Hàm api.getUser[] trả về một promise object
  return new Promise[[resolve, reject] => {
    // Gửi AJAX request
    http.get[`/users/${username}`, [err, result] => {

      // Nếu có lỗi bên trong callback, chúng ta gọi đến hàm `reject[]`
      if [err] return reject[err]

      // Ngược lại, dùng `resolve[]` để trả dữ liệu về cho `.then[]`
      resolve[result]

    }]
  }]
}
6. Trường hợp tương tự:

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
5

Cách giải quyết:

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
6

Chúng ta sẽ được kết quả như ý.

Cẩn thận với 
function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
0 khi dùng tham chiếu hàm

Giả sử bạn có đoạn code sau:

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
7

Hàm 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
9 không làm gì khác ngoài việc chuyển 
function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
2 vào cho 
function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
3, nên bạn có thể dùng tham chiếu hàm để đoạn code trên gọn hơn.

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
8

Bạn có thể nghĩ, vậy với phương thức của một đối tượng, ta cũng có thể đưa tham chiếu hàm vào 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7?

import asyncio

async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]

async def aio_all[seq]:
  for f in seq:
    await f

async def main[]:
  await aio_all[[bar[i] for i in range[10]]]

loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
9

Nhưng bạn lại nhận được lỗi sau:

Unhandled rejection:[TypeError: Cannot read property ‘user’ of undefined]

Lý do là vì khi trong strict mode, biến ngữ cảnh 

function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
0 chỉ được xác định khi trực tiếp gọi phương thức của đối tượng đó, hoặc thông qua 
function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
6. Bạn có thể xem giải thích chi tiết hơn ở đây.

Để giải quyết lỗi này, bạn có thể dùng một trong những cách sau:

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
0

Chạy các Promise tuần tự

Trong trường hợp muốn chạy các promises một cách tuần tự như sơ đồ ở trên, bạn có thể dùng hàm 

function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
7 .

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
1

Async/await mang đến giải pháp “xinh đẹp” hơn, cho phép bạn truy xuất đến giá trị của các promises phía trước nếu cần thiết.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
2

Chạy nhiều Promises cùng lúc với Promise.all[]

Lại có trường hợp bạn muốn thực thi và lấy ra kết quả của nhiều promises cùng lúc. Giải pháp “ngây thơ” sẽ là dùng vòng lặp, hoặc 

function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
8.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
3

Lý do là vì khi promise chưa kịp resolve thì dòng 

function onSuccess[user] { console.log[user] }
function onError[err] { console.error[error] }

api.getUser['pikalong']
  .then[onSuccess, onError]
9 đã chạy rồi. Chúng ta có thể sửa bằng cách dùng 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
0. Phương thức này nhận vào một mảng các promises và chỉ resolve khi tất cả các promises này hoàn thành, hoặc reject khi một trong số chúng xảy ra lỗi.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
4

Nếu dùng async/await thì…

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
5

Đừng quên Promise.race[]

Ngoài hai kiểu chạy tuần tự và song song ở trên, chúng ta còn có 

promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
1. Phương thức này nhận vào một mảng các promises và sẽ resolve/reject ngay khi một trong số các promises này hoàn thành/xảy ra lỗi.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
6

Cẩn thận với 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
2 không tường minh

Xét hai đoạn mã sau:

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
7

Đoạn mã thứ hai trả về 

const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
3 vì trong JavaScript nếu một hàm không công khai trả về một giá trị, 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
3 mặc định sẽ được trả về [nguồn]. Do đó, bạn cần lưu ý về giá trị 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
2 khi làm việc với Promise.

Phân biệt 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
6 và 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
7

Hàm 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
5 trong 
promise[]
  .then[[] => { return 'foo' }]
  .then[result1 => {
     console.log[result1] // 'foo'
     return anotherPromise[]
  }]
  .then[result2 => console.log[result2]] // `result2` sẽ là kết quả của anotherPromise[]
  .catch[err => {}]
6 chỉ có thể chụp được lỗi từ những 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7 phía trước nó, mà không thể bắt được lỗi xảy ra trong hàm 
api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
4 cùng cấp.

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
8

Lưu ý là promise sẽ dừng quá trình thực thi khi bắt được lỗi

import asyncio
async def bar[i]:
  print['started', i]
  await asyncio.sleep[1]
  print['finished', i]
async def main[]:
  await asyncio.gather[*[bar[i] for i in range[10]]]
loop = asyncio.get_event_loop[]
loop.run_until_complete[main[]]
loop.close[]
9

Truyền dữ liệu giữa các promises với nhau

Một trong những yếu điểm của Promise là không có cơ chế mặc định để bạn truyền dữ liệu giữa các promise objects với nhau. Nghĩa là:

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
0

Một cách là dùng 

const p = Promise.resolve[12]
  .then[result => console.log[result]] // 12
  .then[res => Promise.reject[new Error['Dừng lại nhanh']]]
  .then[[] => 'Cười thêm phát nữa là tym anh đứt phanh']
  .catch[err => console.error[err]] // Error: Dừng lại nhanh
2.

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
1

Hoặc, nếu bạn cảm thấy phân tách mảng khó dùng vì phải nhớ thứ tự của các giá trị thì ta có thể dùng object như sau:

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
2

Lại một lần nữa, async/await lại tỏa sáng vì giúp bạn truy xuất đến kết quả của những promises phía trước.

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
3

Cẩn thận nha, Promise không lazy

Với đoạn code sau:

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
4

Kết quả được in ra console lần lượt sẽ là:

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
5

Bạn có thể thấy hàm 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
3 của Promise được thực thi ngay lập tức. Điều này có thể dẫn đến những kết quả không mong muốn, chẳng hạn như:

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
6

Cách giải quyết là đưa vào một hàm trả về promise.

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
7

Cuối cùng, 
const p = Promise.resolve[12]
  .then[result => console.log[result]] // 12
  .then[res => Promise.reject[new Error['Dừng lại nhanh']]]
  .then[[] => 'Cười thêm phát nữa là tym anh đứt phanh']
  .catch[err => console.error[err]] // Error: Dừng lại nhanh
4

Bên cạnh 

api.getUser['pikalong']
  .then[user => api.getPostsOfUser[user]]
  .then[posts => api.getCommentsOfPosts[posts]]
  .catch[err => { throw err }]
7 và 
const p = new Promise[ /* executor */ function[resolve, reject] {
  // Thực thi các tác vụ bất đồng bộ ở đây, và gọi `resolve[result]` khi tác
  // vụ hoàn thành. Nếu xảy ra lỗi, gọi đến `reject[error]`.
}]
4, chúng ta còn có 
const p = Promise.resolve[12]
  .then[result => console.log[result]] // 12
  .then[res => Promise.reject[new Error['Dừng lại nhanh']]]
  .then[[] => 'Cười thêm phát nữa là tym anh đứt phanh']
  .catch[err => console.error[err]] // Error: Dừng lại nhanh
7. Phương thức này nhận vào một hàm và sẽ được kích hoạt dù cho promise trước nó hoàn thành hay xảy ra lỗi.

api.getUser['pikalong', function[err, user] {
  if [err] throw err
  api.getPostsOfUser[user, function[err, posts] {
    if [err] throw err
    api.getCommentsOfPosts[posts, function[err, comments] {
      // vân vân và mây mây...
    }]
  }]
}]
8

Bạn có thể đọc thêm về 

const p = Promise.resolve[12]
  .then[result => console.log[result]] // 12
  .then[res => Promise.reject[new Error['Dừng lại nhanh']]]
  .then[[] => 'Cười thêm phát nữa là tym anh đứt phanh']
  .catch[err => console.error[err]] // Error: Dừng lại nhanh
8 ở đây. Lưu ý là phương thức này hiện chỉ được hỗ trợ bởi Firefox, Chrome và Opera thôi nhé.

Bạn có thể thấy Promise và async/await không hoàn toàn thay thế mà hỗ trợ lẫn nhau. Mặc dù chúng ta có thể dùng async/await ở đa số các trường hợp, Promise vẫn là nền tảng cần thiết khi thực thi các tác vụ bất đồng bộ trong JavaScript. Do đó bạn nên xem xét và lựa chọn giải pháp phù hợp, tùy vào tình hình thực tế nhá.

Các vị trí tuyển dụng lập trình viên Javascript lương cao cho bạn

Bài viết gốc được đăng trên ehkoo.com

Bài Viết Liên Quan

Chủ Đề