Việc sử dụng async / await bên trong một hàm tạo Promise () mới có phải là một mẫu chống không?


94

Tôi đang sử dụng async.eachLimitchức năng này để kiểm soát số lượng hoạt động tối đa tại một thời điểm.

const { eachLimit } = require("async");

function myFunction() {
 return new Promise(async (resolve, reject) => {
   eachLimit((await getAsyncArray), 500, (item, callback) => {
     // do other things that use native promises.
   }, (error) => {
     if (error) return reject(error);
     // resolve here passing the next value.
   });
 });
}

Như bạn thấy, tôi không thể khai báo myFunctionhàm là không đồng bộ vì tôi không có quyền truy cập vào giá trị bên trong lệnh gọi lại thứ hai của eachLimithàm.


"Như bạn thấy, tôi không thể khai báo myFunction là async" --- bạn có thể giải thích thêm được không?
zerkms

1
Ồ, được rồi ... xin lỗi. Tôi cần hàm tạo vì tôi cần async.eachLimit để tránh hơn 500 hoạt động không đồng bộ cùng một lúc. Tôi đang tải xuống và trích xuất dữ liệu từ các tệp văn bản và tôi muốn tránh nhiều hoạt động không đồng bộ, sau khi trích xuất dữ liệu, tôi phải trả lại Lời hứa kèm theo dữ liệu và tôi sẽ không thể trả lại nó từ lệnh gọi lại của async.eachLimit .

1. Tại sao bạn cần chờ đợi? Không đồng bộ đã là một cơ chế luồng kiểm soát. 2. Nếu bạn muốn sử dụng async.js với lời hứa bên Node.js hãy nhìn vào async-q
slebetman

Để tránh địa ngục gọi lại, và nếu có gì đó ném ra, thì lời hứa bên ngoài sẽ bắt được.

Câu trả lời:


82

Bạn đang sử dụng hiệu quả các hứa hẹn bên trong hàm thực thi phương thức khởi tạo hứa, vì vậy đây là mẫu phản phương thức khởi tạo Promise .

Mã của bạn là một ví dụ điển hình về rủi ro chính: không phổ biến tất cả các lỗi một cách an toàn. Đọc tại sao ở đó .

Ngoài ra, việc sử dụng async/ awaitcó thể làm cho những cái bẫy giống nhau thậm chí còn đáng ngạc nhiên hơn. So sánh:

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

với asynctương đương ngây thơ (sai) :

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

Tìm bảng điều khiển web của trình duyệt của bạn để biết bảng điều khiển cuối cùng.

Cách đầu tiên hoạt động vì bất kỳ ngoại lệ ngay lập tức nào trong hàm thực thi của phương thức khởi tạo Promise sẽ từ chối một cách thuận tiện lời hứa mới được xây dựng (nhưng bên trong bất kỳ ngoại lệ nào .thenmà bạn tự làm).

Cái thứ hai không hoạt động bởi vì bất kỳ ngoại lệ tức thời nào trong một asynchàm đều từ chối lời hứa ngầm được trả về bởi asyncchính hàm .

Vì giá trị trả về của một hàm thực thi phương thức khởi tạo hứa không được sử dụng, đó là tin xấu!

Ma cua ban

Không có lý do gì bạn không thể định nghĩa myFunctionasync:

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

Mặc dù tại sao lại sử dụng các thư viện điều khiển đồng thời lỗi thời khi bạn có await?


12
Bạn không cần return await: return new Promiselà đủ.
một

2
Tôi chính thức chấp thuận câu trả lời này, tôi cũng đã nói chính xác như vậy :-)
Bergi

1
@celoxxx Hãy xem ở đây . Bạn thực sự không bao giờ nên sử dụng async.js với lời hứa
Bergi

1
@celoxxx Chỉ cần bỏ các loại và nó trở thành js đơn giản. Bạn không nên sử dụng async.js vì các giao diện khác nhau - gọi lại kiểu nút và hứa hẹn - gây ra quá nhiều rắc rối và dẫn đến mã phức tạp và dễ xảy ra lỗi không cần thiết.
Bergi

1
Tôi đồng ý với bạn ... Nhưng mã này đã cũ và tôi đang cấu trúc lại để sử dụng sự kiện + async.js (để kiểm soát giới hạn của async. Nếu bạn biết cách tốt hơn, vui lòng nói).

16

Tôi đồng ý với các câu trả lời được đưa ra ở trên và tuy nhiên, đôi khi việc không đồng bộ hóa bên trong lời hứa của bạn sẽ tốt hơn, đặc biệt nếu bạn muốn xâu chuỗi một số thao tác trả lại lời hứa và tránh điều then().then()quái quỷ. Tôi sẽ xem xét sử dụng một cái gì đó như thế này trong tình huống đó:

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. Hàm được truyền cho hàm Promisetạo không phải là không đồng bộ, vì vậy các linters không hiển thị lỗi.
  2. Tất cả các hàm không đồng bộ có thể được gọi theo thứ tự tuần tự bằng cách sử dụng await.
  3. Các lỗi tùy chỉnh có thể được thêm vào để xác thực kết quả của các hoạt động không đồng bộ
  4. Cuối cùng thì lỗi cũng được phát hiện.

Tuy nhiên, một hạn chế là bạn phải nhớ đặt try/catchvà gắn nó vào reject.


4
static getPosts(){
    return new Promise( (resolve, reject) =>{
        try {
            const res =  axios.get(url);
            const data = res.data;
            resolve(
                data.map(post => ({
                    ...post,
                    createdAt: new Date(post.createdAt)
                }))
            )
        } catch (err) {
            reject(err);                
        }
    })
}

loại bỏ await và async sẽ giải quyết vấn đề này. bởi vì bạn đã áp dụng đối tượng Promise, vậy là đủ.


Vì vậy, trong ví dụ của bạn, sẽ axios.get(url)hoạt động như thể nó được gọi là await axios.get(url)?
PrestonDocks
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.