มันเป็นการต่อต้านรูปแบบที่จะใช้ async / await ภายในตัวสร้าง Promise () ใหม่หรือไม่?


93

ฉันกำลังใช้async.eachLimitฟังก์ชันเพื่อควบคุมจำนวนการดำเนินการสูงสุดในแต่ละครั้ง

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.
   });
 });
}

อย่างที่คุณเห็นฉันไม่สามารถประกาศmyFunctionฟังก์ชันเป็น async ได้เนื่องจากฉันไม่สามารถเข้าถึงค่าภายในการเรียกกลับครั้งที่สองของeachLimitฟังก์ชันได้


"อย่างที่คุณเห็นฉันไม่สามารถประกาศ myFunction เป็น async ได้" --- คุณสามารถอธิบายเพิ่มเติมได้ไหม
zerkms

1
โอเค ... ขอโทษ ฉันต้องการตัวสร้างเพราะฉันต้องการ async.eachLimit เพื่อหลีกเลี่ยงการดำเนินการแบบอะซิงโครนัสมากกว่า 500 ครั้งในแต่ละครั้ง ฉันกำลังดาวน์โหลดและแยกข้อมูลจากไฟล์ข้อความและฉันต้องการหลีกเลี่ยงการดำเนินการแบบอะซิงโครนัสมากนักหลังจากที่ฉันดึงข้อมูลฉันต้องส่งคืน Promise พร้อมกับข้อมูลและฉันจะไม่สามารถส่งคืนได้จากการเรียกกลับของ async .

1. ทำไมคุณต้องรอ? Async เป็นกลไกควบคุมโฟลว์อยู่แล้ว 2. หากคุณต้องการใช้ async.js กับสัญญาภายใน node.js ให้ดูที่ async-q
slebetman

เพื่อหลีกเลี่ยงการเรียกกลับนรกและหากมีบางอย่างเกิดขึ้นสัญญาภายนอกก็จะจับได้

คำตอบ:


81

คุณได้อย่างมีประสิทธิภาพโดยใช้สัญญาภายในฟังก์ชั่นคอนสตรัคผู้จัดการสัญญาดังนั้นนี้สัญญาคอนสตรัคต่อต้านรูปแบบ

รหัสของคุณเป็นตัวอย่างที่ดีของความเสี่ยงหลัก: ไม่เผยแพร่ข้อผิดพลาดทั้งหมดอย่างปลอดภัย อ่านทำไมมี

นอกจากนี้การใช้async/ awaitสามารถสร้างกับดักเดียวกันได้อย่างน่าประหลาดใจยิ่งขึ้น เปรียบเทียบ:

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

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

ด้วยความไร้เดียงสา (ผิด) asyncเทียบเท่า:

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

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

ดูในเว็บคอนโซลของเบราว์เซอร์เป็นรายการสุดท้าย

อันแรกใช้งานได้เนื่องจากข้อยกเว้นใด ๆ ในทันทีในฟังก์ชันตัวดำเนินการตัวสร้างสัญญาจะปฏิเสธสัญญาที่สร้างขึ้นใหม่อย่างสะดวก (แต่ภายในสิ่งที่.thenคุณอยู่เอง)

คนที่สองไม่ได้เพราะข้อยกเว้นใด ๆ ทันทีในasyncฟังก์ชั่นปฏิเสธสัญญาโดยปริยายส่งกลับโดยasyncฟังก์ชั่นของตัวเอง

เนื่องจากไม่ได้ใช้ค่าที่ส่งคืนของฟังก์ชันตัวดำเนินการตัวสร้างสัญญาจึงเป็นข่าวร้าย

รหัสของคุณ

ไม่มีเหตุผลใดที่คุณไม่สามารถกำหนดได้myFunctionว่าasync:

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.
    });
  });
}

ทำไมต้องใช้ไลบรารีควบคุมการทำงานพร้อมกันที่ล้าสมัยเมื่อคุณมีawait?


12
คุณไม่ต้องการreturn await: return new Promiseก็เพียงพอแล้ว
lonesomeday

2
ฉันอนุมัติคำตอบนี้อย่างเป็นทางการฉันได้พูดเหมือนกันทุก
ประการ

1
@celoxxx มีลักษณะที่นี่ คุณไม่ควรใช้ async.js กับสัญญา
เด็ดขาด

1
@celoxxx เพียงแค่วางประเภทและมันจะกลายเป็น js ธรรมดา คุณไม่ควรใช้ async.js เนื่องจากอินเทอร์เฟซที่แตกต่างกัน - การเรียกกลับแบบโหนดเทียบกับสัญญา - ทำให้เกิดแรงเสียดทานมากเกินไปและนำไปสู่รหัสที่ซับซ้อนโดยไม่จำเป็นและเกิดข้อผิดพลาดได้
Bergi

1
ฉันเห็นด้วยกับคุณ ... แต่รหัสนี้เก่าและฉันกำลัง refactoring เพื่อใช้ events + async.js (เพื่อควบคุมขีด จำกัด ของ async แต่ถ้าคุณรู้วิธีที่ดีกว่านี้โปรดพูด)

16

ฉันเห็นด้วยกับคำตอบที่ให้ไว้ข้างต้นและถึงกระนั้นบางครั้งก็ไม่ชัดเจนที่จะมีการ async ในสัญญาของคุณโดยเฉพาะอย่างยิ่งถ้าคุณต้องการผูกมัดการดำเนินการหลายอย่างที่คืนสัญญาและหลีกเลี่ยงthen().then()นรก ฉันจะพิจารณาใช้สิ่งนี้ในสถานการณ์นั้น:

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. ฟังก์ชันที่ส่งไปยังตัวPromiseสร้างไม่ได้เป็นแบบ async ดังนั้น linters จึงไม่แสดงข้อผิดพลาด
  2. สามารถเรียกใช้ฟังก์ชัน async ทั้งหมดตามลำดับโดยใช้awaitไฟล์.
  3. สามารถเพิ่มข้อผิดพลาดที่กำหนดเองเพื่อตรวจสอบผลลัพธ์ของการดำเนินการ async
  4. ข้อผิดพลาดถูกจับได้ในที่สุด

แม้ว่าข้อเสียเปรียบคือการที่คุณต้องจำไว้วางและแนบไปtry/catchreject


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);                
        }
    })
}

ลบ await และ async จะแก้ปัญหานี้ เนื่องจากคุณได้ใช้วัตถุ Promise นั่นเพียงพอแล้ว


ดังนั้นในตัวอย่างของคุณจะaxios.get(url)ทำงานเหมือนกับที่เรียกว่าawait axios.get(url)?
PrestonDocks
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.