การจัดการข้อผิดพลาดใน Promise.all


266

ฉันมีคำสัญญามากมายที่ฉันแก้ไขด้วย Promise.all(arrayOfPromises);

ฉันไปเพื่อดำเนินการต่อสัญญาโซ่ ดูเหมือนอะไรแบบนี้

existingPromiseChain = existingPromiseChain.then(function() {
  var arrayOfPromises = state.routes.map(function(route){
    return route.handler.promiseHandler();
  });
  return Promise.all(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
  // do stuff with my array of resolved promises, eventually ending with a res.send();
});

ฉันต้องการเพิ่มคำสั่ง catch เพื่อจัดการแต่ละสัญญาในกรณีที่เกิดข้อผิดพลาด แต่เมื่อฉันลองPromise.allส่งคืนข้อผิดพลาดแรกที่พบ (ไม่สนใจส่วนที่เหลือ) จากนั้นฉันไม่สามารถรับข้อมูลจากส่วนที่เหลือของสัญญาใน อาร์เรย์ (ที่ไม่ผิดพลาด)

ฉันพยายามทำบางสิ่งเช่น ..

existingPromiseChain = existingPromiseChain.then(function() {
      var arrayOfPromises = state.routes.map(function(route){
        return route.handler.promiseHandler()
          .then(function(data) {
             return data;
          })
          .catch(function(err) {
             return err
          });
      });
      return Promise.all(arrayOfPromises)
    });

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
      // do stuff with my array of resolved promises, eventually ending with a res.send();
});

แต่นั่นก็ไม่ได้แก้ไข

ขอบคุณ!

-

แก้ไข:

สิ่งที่คำตอบด้านล่างนี้พูดนั้นเป็นความจริงอย่างสมบูรณ์รหัสก็ผิดปกติเนื่องจากเหตุผลอื่น ในกรณีที่ใครสนใจนี่เป็นคำตอบที่ฉันลงเอยด้วย ...

โหนดเซิร์ฟเวอร์ Express

serverSidePromiseChain
    .then(function(AppRouter) {
        var arrayOfPromises = state.routes.map(function(route) {
            return route.async();
        });
        Promise.all(arrayOfPromises)
            .catch(function(err) {
                // log that I have an error, return the entire array;
                console.log('A promise failed to resolve', err);
                return arrayOfPromises;
            })
            .then(function(arrayOfPromises) {
                // full array of resolved promises;
            })
    };

API Call (สาย route.async)

return async()
    .then(function(result) {
        // dispatch a success
        return result;
    })
    .catch(function(err) {
        // dispatch a failure and throw error
        throw err;
    });

วาง.catchไว้Promise.allก่อนหน้า.thenดูเหมือนว่าจะมีวัตถุประสงค์ในการจับข้อผิดพลาดใด ๆ จากสัญญาเดิม แต่แล้วกลับอาร์เรย์ทั้งหมดไปยังถัดไป.then

ขอบคุณ!


2
ความพยายามของคุณดูเหมือนว่ามันจะทำงานได้ ... อาจจะมีปัญหาอื่นอีกไหม?
Ry-

.then(function(data) { return data; })สามารถละเว้นได้อย่างสมบูรณ์
Bergi

เหตุผลเดียวที่ไม่ควรแก้ไขข้างต้นคือถ้าคุณไม่แสดงรหัสทั้งหมดในตัวจัดการthenหรือcatchตัวจัดการและมีข้อผิดพลาดเกิดขึ้นภายใน โดยวิธีนี้โหนดนี้หรือไม่

1
คุณไม่มีการจับครั้งสุดท้ายใน "ห่วงโซ่ที่มีอยู่" ของคุณดังนั้นอาจมีข้อผิดพลาดที่คุณไม่เห็นซึ่งอาจอธิบายได้ว่าทำไม "ไม่แก้ไข" ลองเพิ่มและดูว่าเกิดข้อผิดพลาดอะไร
jib

นี่คือคำตอบ: stackoverflow.com/questions/31424561/ …
Humoyun Ahmad

คำตอบ:


189

Promise.allคือทั้งหมดหรือไม่มีอะไรเลย จะแก้ไขเมื่อสัญญาทั้งหมดในการแก้ไขอาร์เรย์หรือปฏิเสธทันทีที่หนึ่งในนั้นปฏิเสธ กล่าวอีกนัยหนึ่งมันสามารถแก้ไขได้ด้วยอาร์เรย์ของค่าที่แก้ไขทั้งหมดหรือปฏิเสธด้วยข้อผิดพลาดเดียว

ห้องสมุดบางแห่งมีสิ่งที่เรียกว่าPromise.whenซึ่งฉันเข้าใจว่าจะรอให้สัญญาทั้งหมดในอาร์เรย์นั้นแก้ไขหรือปฏิเสธ แต่ฉันไม่คุ้นเคยกับมันและไม่ใช่ ES6

รหัสของคุณ

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

เหตุผลเดียวที่ฉันคิดได้ว่าทำไม "ไม่แก้ไข" ก็คือความล้มเหลวในโค้ดที่คุณไม่ได้แสดงให้เราเห็นและสาเหตุที่คุณไม่เห็นข้อความแสดงข้อผิดพลาดเกี่ยวกับเรื่องนี้เป็นเพราะห่วงโซ่คำสัญญานี้ไม่สิ้นสุด จับ (เท่าที่คุณกำลังแสดงให้เราเห็น)

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

Promise.all(state.routes.map(function(route) {
  return route.handler.promiseHandler().catch(function(err) {
    return err;
  });
}))
.then(function(arrayOfValuesOrErrors) {
  // handling of my array containing values and/or errors. 
})
.catch(function(err) {
  console.log(err.message); // some coding error in handling happened
});

4
คุณ (และความคิดเห็นข้างต้น) ถูกต้อง route.handler.promiseHandler ของฉันจำเป็นต้องใช้เพื่อ. catch () และส่งคืนข้อผิดพลาด ฉันยังจำเป็นต้องเพิ่ม. catch () สุดท้ายลงในส่วนท้ายของห่วงโซ่ ขอบคุณที่ถ่ายทอดความสำคัญของการมีตัวจัดการความสำเร็จ / ข้อผิดพลาดในทุกขั้นตอนของโซ่ :)
Jon

2
ฉันพบว่าหากฉันโยนข้อผิดพลาดใน. catch () ของฉันสำหรับ route.handler.promiseHandler มันจะไปที่การจับครั้งสุดท้ายโดยอัตโนมัติ ถ้าฉันกลับข้อผิดพลาดแทนมันจะทำสิ่งที่ฉันต้องการและจัดการกับอาร์เรย์ทั้งหมด
Jon

2
ขณะนี้มีวิธีมาตรฐานPromise.allSettled()พร้อมการสนับสนุนที่ดี ดูอ้างอิง
Andréa Maugars

ใช่Promise.allล้มเหลวเมื่อเธรดแรกล้มเหลว แต่น่าเสียดายที่เธรดอื่น ๆ ทั้งหมดยังคงทำงานต่อไปจนกว่าจะเสร็จสิ้น ไม่มีสิ่งใดถูกยกเลิกยิ่งแย่กว่านี้คือ: ไม่มีวิธีที่จะยกเลิกเธรดPromiseได้ ดังนั้นสิ่งที่เธรดกำลังทำ (และจัดการ) จะดำเนินการต่อพวกเขาเปลี่ยนสถานะและตัวแปรพวกเขาใช้ CPU แต่ท้ายที่สุดพวกเขาจะไม่ส่งคืนผลลัพธ์ คุณต้องระวังสิ่งนี้เพื่อไม่ให้เกิดความวุ่นวายเช่นเมื่อคุณโทรซ้ำ / ลองอีกครั้ง
Marc Wäckerlin

143

คำตอบใหม่

const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));

API สัญญาในอนาคต

  • Chrome 76: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
  • คุณสามารถดาวน์โหลดhttps://www.npmjs.com/package/promise.allsettledเพื่อรับทันที ในเบราว์เซอร์บางเบราว์เซอร์ทั้งหมดมาพร้อมกับเบราว์เซอร์เอง มันคุ้มค่าที่จะดาวน์โหลดแพ็คเกจเพื่อความอุ่นใจเพราะเช่น TypeScript ไม่มีคำจำกัดความเริ่มต้นสำหรับการตั้งค่าทั้งหมด

11
แม้ว่าจะไม่จำเป็นต้องเป็นe มันอาจจะเป็นสตริงตัวอย่างเช่นถ้าผลตอบแทนที่มีคนชอบมันError Promise.reject('Service not available')
Klesun

@ArturKlesun แล้วเราจะแยกแยะว่าสัญญาใดที่ส่งผลให้เกิดข้อผิดพลาดและไม่ได้?
Shubham Jain

5
@ shubham-jain ด้วยและ.then() จะส่งผ่านค่าไปยังอดีตในขณะที่จะส่งผ่านไปยังหลัง คุณสามารถห่อไว้ในวัตถุตัวอย่างเช่น: .catch()Promise.resolve()Promise.reject()p.then(v => ({success: true, value: v})).catch(e => ({success: false, error: e}))
Klesun

2
ทำไมคุณต้องกรองผลลัพธ์ ไม่มีเหตุผลหากคุณทำอะไรกับผลลัพธ์คุณต้องมีคำสั่งเพื่อทราบว่ามูลค่าที่ส่งคืนมาจากสัญญาใด!
Ryan Taylor

21

เพื่อดำเนินการต่อPromise.allห่วง (แม้ในขณะที่สัญญาปฏิเสธ) executeAllPromisesผมเขียนฟังก์ชั่นยูทิลิตี้ที่เรียกว่า ฟังก์ชั่นยูทิลิตี้นี้จะส่งคืนวัตถุที่มีและresultserrors

แนวคิดก็คือสัญญาทั้งหมดที่คุณส่งให้ executeAllPromisesจะถูกรวมไว้ในสัญญาใหม่ซึ่งจะแก้ไขได้ตลอดเวลา สัญญาใหม่แก้ไขด้วยอาเรย์ที่มี 2 สปอต จุดแรกถือค่าการแก้ไข (ถ้ามี) และจุดที่สองเก็บข้อผิดพลาด (ถ้าสัญญาที่ห่อไว้ปฏิเสธ)

เป็นขั้นตอนสุดท้ายที่executeAllPromisesสะสมค่าทั้งหมดของคำมั่นสัญญาที่ห่อไว้และส่งคืนวัตถุสุดท้ายด้วยอาร์เรย์สำหรับresultsและอาร์เรย์สำหรับerrorsและอาเรย์สำหรับ

นี่คือรหัส:

function executeAllPromises(promises) {
  // Wrap all Promises in a Promise that will always "resolve"
  var resolvingPromises = promises.map(function(promise) {
    return new Promise(function(resolve) {
      var payload = new Array(2);
      promise.then(function(result) {
          payload[0] = result;
        })
        .catch(function(error) {
          payload[1] = error;
        })
        .then(function() {
          /* 
           * The wrapped Promise returns an array:
           * The first position in the array holds the result (if any)
           * The second position in the array holds the error (if any)
           */
          resolve(payload);
        });
    });
  });

  var errors = [];
  var results = [];

  // Execute all wrapped Promises
  return Promise.all(resolvingPromises)
    .then(function(items) {
      items.forEach(function(payload) {
        if (payload[1]) {
          errors.push(payload[1]);
        } else {
          results.push(payload[0]);
        }
      });

      return {
        errors: errors,
        results: results
      };
    });
}

var myPromises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.reject(new Error('3')),
  Promise.resolve(4),
  Promise.reject(new Error('5'))
];

executeAllPromises(myPromises).then(function(items) {
  // Result
  var errors = items.errors.map(function(error) {
    return error.message
  }).join(',');
  var results = items.results.join(',');
  
  console.log(`Executed all ${myPromises.length} Promises:`);
  console.log(`— ${items.results.length} Promises were successful: ${results}`);
  console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});


2
สามารถทำได้ง่ายกว่านี้ ดูstackoverflow.com/a/36115549/918910
jib

18

ES2020แนะนำวิธีการใหม่สำหรับประเภทสัญญา: Promise.allSettled()
Promise.allSettled ให้สัญญาณเมื่อสัญญาอินพุตทั้งหมดถูกตัดสินซึ่งหมายความว่าพวกเขาปฏิบัติตามหรือปฏิเสธ สิ่งนี้มีประโยชน์ในกรณีที่คุณไม่สนใจสถานะของสัญญาคุณแค่อยากรู้ว่าเมื่องานเสร็จไม่ว่าจะประสบความสำเร็จหรือไม่

const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.

const result = await Promise.allSettled(promises);
console.log(result.map(x=>s.status));
// ['fulfilled', 'fulfilled', 'rejected']

อ่านเพิ่มเติมในโพสต์บล็อก v8 https://v8.dev/features/promise-combinators


13

ตามที่ @ jib กล่าวว่า

Promise.all คือทั้งหมดหรือไม่มีอะไรเลย

แม้ว่าคุณจะสามารถควบคุมสัญญาบางอย่างที่ "อนุญาต" .thenล้มเหลวและเราต้องการที่จะดำเนินการต่อไป

ตัวอย่างเช่น.

  Promise.all([
    doMustAsyncTask1,
    doMustAsyncTask2,
    doOptionalAsyncTask
    .catch(err => {
      if( /* err non-critical */) {
        return
      }
      // if critical then fail
      throw err
    })
  ])
  .then(([ mustRes1, mustRes2, optionalRes ]) => {
    // proceed to work with results
  })

6

หากคุณได้ใช้ห้องสมุด q https://github.com/kriskowal/q จะมีวิธีการ q.allSettled () ที่สามารถแก้ปัญหานี้ได้คุณสามารถจัดการทุกสัญญาได้ขึ้นอยู่กับสถานะของไฟล์ทั้งไฟล์หรือปฏิเสธ

existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
  return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
   if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
     //do somthing
   } else {
     // do something else
   }
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});

เนื่องจากคุณแนะนำให้ใช้ห้องสมุดบางแห่ง ( q) มันจะมีประโยชน์มากกว่าถ้าคุณให้ตัวอย่างการใช้งานที่เกี่ยวข้องกับคำถาม คำตอบของคุณไม่ได้อธิบายว่าห้องสมุดนี้สามารถช่วยแก้ปัญหาได้อย่างไร
ishmaelMakitla

เพิ่มตัวอย่างตามที่แนะนำ
Mohamed Mahmoud

1
ในปี 2018 มีคนประมาณหนึ่งคนที่เห็นว่า Sindre มีให้บริการ :-) github.com/sindresorhus/p-settle ด้วยโมดูลจุดประสงค์เดียวของ Sindre คุณไม่จำเป็นต้องนำเข้าไลบรารีขนาดใหญ่เช่น q เพียงแค่บิตเดียว
DKebler

6

การใช้ Async กำลังรอคอย -

ที่นี่หนึ่งฟังก์ชัน async func1 กำลังคืนค่าที่ได้รับการแก้ไขและ func2 กำลังส่งข้อผิดพลาดและคืนค่า null ในสถานการณ์นี้เราสามารถจัดการกับวิธีที่เราต้องการและส่งคืนได้ตามนั้น

const callingFunction  = async () => {
    const manyPromises = await Promise.all([func1(), func2()]);
    console.log(manyPromises);
}


const func1 = async () => {
    return 'func1'
}

const func2 = async () => {
    try {
        let x;
        if (!x) throw "x value not present"
    } catch(err) {
       return null
    }
}

callingFunction();

ผลลัพธ์คือ - ['func1', null]


4

สำหรับผู้ที่ใช้ ES8 ที่สะดุดที่นี่คุณสามารถทำสิ่งต่อไปนี้โดยใช้ฟังก์ชั่น async :

var arrayOfPromises = state.routes.map(async function(route){
  try {
    return await route.handler.promiseHandler();
  } catch(e) {
    // Do something to handle the error.
    // Errored promises will return whatever you return here (undefined if you don't return anything).
  }
});

var resolvedPromises = await Promise.all(arrayOfPromises);

3

เราสามารถจัดการการปฏิเสธที่ระดับสัญญาแต่ละรายการดังนั้นเมื่อเราได้ผลลัพธ์ในอาร์เรย์ผลลัพธ์ของเราดัชนีอาร์เรย์ที่ถูกปฏิเสธจะเป็น undefinedดัชนีอาร์เรย์ซึ่งได้รับการปฏิเสธที่จะเป็น เราสามารถจัดการกับสถานการณ์นั้นได้ตามต้องการและใช้ผลลัพธ์ที่เหลืออยู่

ที่นี่ฉันได้ปฏิเสธสัญญาแรกดังนั้นจึงเป็นเหมือนไม่ได้กำหนด แต่เราสามารถใช้ผลลัพธ์ของสัญญาที่สองซึ่งอยู่ที่ดัชนี 1

const manyPromises = Promise.all([func1(), func2()]).then(result => {
    console.log(result[0]);  // undefined
    console.log(result[1]);  // func2
});

function func1() {
    return new Promise( (res, rej) => rej('func1')).catch(err => {
        console.log('error handled', err);
    });
}

function func2() {
    return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}


คุณจะทำสิ่งที่คล้ายกันได้อย่างไรถ้าเราใช้ async กำลังรออยู่
Rudresh Ajgaonkar

ฉันได้ตอบคำถามของคุณโปรดหาลิงก์สำหรับคำตอบ stackoverflow.com/a/55216763/4079716
Nayan Patel

2

คุณเคยคิดPromise.prototype.finally()ไหม

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

จากเอกสาร MDN :

finally()วิธีการจะเป็นประโยชน์ถ้าคุณต้องการที่จะดำเนินการบางอย่างหรือการทำความสะอาดครั้งหนึ่งเคยสัญญาจะตัดสินไม่คำนึงถึงผลของมัน

finally()วิธีการคล้ายกับการเรียก.then(onFinally, onFinally)แต่มีคู่ของความแตกต่าง:

เมื่อสร้างฟังก์ชั่นอินไลน์คุณสามารถผ่านได้หนึ่งครั้งแทนที่จะถูกบังคับให้ประกาศทั้งสองครั้งหรือสร้างตัวแปรให้

การโทรกลับในที่สุดจะไม่ได้รับการโต้แย้งใด ๆ เนื่องจากไม่มีวิธีการที่เชื่อถือได้ในการพิจารณาว่าสัญญาได้รับการปฏิบัติตามหรือปฏิเสธ กรณีการใช้งานนี้มีไว้สำหรับเมื่อคุณไม่สนใจเกี่ยวกับเหตุผลการปฏิเสธหรือค่าการปฏิบัติตามดังนั้นจึงไม่จำเป็นต้องระบุ

ซึ่งแตกต่างจากPromise.resolve(2).then(() => {}, () => {})(ซึ่งจะแก้ไขด้วยไม่ได้กำหนด) Promise.resolve(2).finally(() => {})จะได้รับการแก้ไขด้วย 2 ในทำนองเดียวกันซึ่งแตกต่างจากPromise.reject(3).then(() => {}, () => {})(ซึ่งจะได้รับการเติมเต็มด้วยไม่ได้กำหนด) Promise.reject(3).finally(() => {})จะถูกปฏิเสธด้วย 3

== ทางเลือก ==

หาก JavaScript เวอร์ชันของคุณไม่รองรับPromise.prototype.finally()คุณสามารถใช้วิธีแก้ปัญหานี้จากJake Archibald :Promise.all(promises.map(p => p.catch(() => undefined)));


1
ใช่จนกระทั่งPromises.allSettled()มีการนำไปใช้จริง (เอกสารนี้จัดทำโดย MDN ที่นี่ ) จากนั้นPromises.all.finally()ดูเหมือนว่าจะประสบความสำเร็จในสิ่งเดียวกัน ฉันกำลังจะลอง ...
jamess

@jamess ทำไมคุณไม่แสดงความคิดเห็นนี้เป็นคำตอบที่เหมาะสม ไม่มีคำตอบอ้างถึง allSettled()ES6
pravin

@pravin - จากสิ่งที่ฉันสามารถบอกได้ว่าallSettled()ยังไม่มีการใช้งานที่ใดเลย (ยัง) ดังนั้นฉันจึงไม่ต้องการนำหน้าความเป็นจริง ฉันประสบความสำเร็จด้วยPromises.all(myPromiseArray).finally()และนั่นก็เหมาะกับคำตอบนี้ เมื่อallSettled()มีจริงแล้วฉันอาจทดสอบและหาวิธีการใช้งานได้จริง จนกว่าจะถึงตอนนั้นใครจะรู้ว่าเบราว์เซอร์จะใช้งานจริงอย่างไร เว้นแต่คุณจะมีข้อมูลล่าสุดที่ตรงกันข้าม ...
jamess

@jamess True ว่ายังอยู่ในขั้นตอนการร่าง .. แต่ FF ล่าสุดและ chrome ดูเหมือนจะสนับสนุนอย่างเต็มที่ .. ไม่แน่ใจว่ามันจะเสถียรหรือไม่ .. Mozilla Docsยังไงก็ตามจุดที่ฉันพยายามทำคือว่ามันจะหาได้ง่ายกว่ามาก ถ้ามันเป็นคำตอบกว่าแสดงความคิดเห็น .. มันโทร ur แม้ว่า :)
Pravin

@pravin - ในขณะที่ฉันโพสต์ความคิดเห็นของฉันมันไม่ได้ดำเนินการที่ใดก็ได้ ฉันเพิ่งทดสอบใน Firefox และ Chrome: Promise.allSettledไม่ได้ใช้งานใน Firefox แต่ดูเหมือนว่าจะมีอยู่ใน Chrome เพียงเพราะเอกสารกล่าวว่ามีการใช้งานไม่ได้หมายความว่ามีการใช้งานจริง ฉันจะไม่ใช้มันเร็ว ๆ นี้
jamess

0

อีกทางเลือกหนึ่งถ้าคุณมีกรณีที่คุณไม่ใส่ใจเป็นพิเศษเกี่ยวกับคุณค่าของสัญญาที่ได้รับการแก้ไขเมื่อมีความล้มเหลวครั้งเดียว แต่คุณยังต้องการให้พวกเขาทำงานคุณสามารถทำสิ่งนี้ซึ่งจะแก้ไขด้วยสัญญาตามปกติเมื่อ พวกเขาทั้งหมดประสบความสำเร็จและปฏิเสธด้วยสัญญาที่ล้มเหลวเมื่อใด ๆ ของพวกเขาล้มเหลว:

function promiseNoReallyAll (promises) {
  return new Promise(
    async (resolve, reject) => {
      const failedPromises = []

      const successfulPromises = await Promise.all(
        promises.map(
          promise => promise.catch(error => {
            failedPromises.push(error)
          })
        )
      )

      if (failedPromises.length) {
        reject(failedPromises)
      } else {
        resolve(successfulPromises)
      }
    }
  )
}

0

คุณสามารถห่อฟังก์ชั่นการส่งคืนสัญญาของคุณในลักษณะที่พวกเขาจับความล้มเหลวและกลับมาแทนค่าที่ตกลงกันไว้ (เช่น error.message) ดังนั้นข้อยกเว้นจะไม่หมุนไปจนถึงฟังก์ชัน Promise.all และปิดใช้งาน

async function resetCache(ip) {

    try {

        const response = await axios.get(`http://${ip}/resetcache`);
        return response;

    }catch (e) {

        return {status: 'failure', reason: 'e.message'};
    }

}

0

ฉันพบวิธี (วิธีแก้ปัญหา) ในการทำสิ่งนี้โดยไม่ทำให้ข้อมูลตรงกัน

ดังนั้นตามที่ได้รับการกล่าวถึงก่อนหน้าPromise.allนี้คือไม่มีสิ่งใดเลย

ดังนั้น ... ใช้สัญญาที่แนบมาเพื่อจับและบังคับให้แก้ไข


      let safePromises = originalPrmises.map((imageObject) => {
            return new Promise((resolve) => {
              // Do something error friendly
              promise.then(_res => resolve(res)).catch(_err => resolve(err))
            })
        })
    })

    // safe
    return Promise.all(safePromises)

0

คุณจะต้องรู้วิธีระบุข้อผิดพลาดในผลลัพธ์ของคุณ หากคุณไม่มีข้อผิดพลาดมาตรฐานที่คาดไว้ฉันขอแนะนำให้คุณทำการเปลี่ยนแปลงในแต่ละข้อผิดพลาดใน catch block ที่ทำให้สามารถระบุได้ในผลลัพธ์ของคุณ

try {
  let resArray = await Promise.all(
    state.routes.map(route => route.handler.promiseHandler().catch(e => e))
  );

  // in catch(e => e) you can transform your error to a type or object
  // that makes it easier for you to identify whats an error in resArray
  // e.g. if you expect your err objects to have e.type, you can filter
  // all errors in the array eg
  // let errResponse = resArray.filter(d => d && d.type === '<expected type>')
  // let notNullResponse = resArray.filter(d => d)

  } catch (err) {
    // code related errors
  }

0

ไม่ใช่วิธีที่ดีที่สุดในการบันทึกข้อผิดพลาด แต่คุณสามารถตั้งค่าทุกอย่างให้เป็นอาร์เรย์สำหรับสัญญาทั้งหมดและเก็บผลลัพธ์ไว้ในตัวแปรใหม่

หากคุณใช้ graphQL คุณจำเป็นต้องประมวลผลการตอบกลับโดยไม่คำนึงถึงและหากไม่พบการอ้างอิงที่ถูกต้องมันจะทำให้แอปล่มให้แคบลงว่าปัญหาอยู่ที่ใด

const results = await Promise.all([
  this.props.client.query({
    query: GET_SPECIAL_DATES,
  }),
  this.props.client.query({
    query: GET_SPECIAL_DATE_TYPES,
  }),
  this.props.client.query({
    query: GET_ORDER_DATES,
  }),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;

-1

นั่นเป็นวิธีที่Promise.allออกแบบมาเพื่อทำงาน หากคำสัญญาเดียวreject()วิธีทั้งหมดจะล้มเหลวทันที

มีกรณีการใช้งานที่หนึ่งอาจต้องการที่จะมีการPromise.allอนุญาตให้สัญญาที่จะล้มเหลว ในการทำให้สิ่งนี้เกิดขึ้นอย่าใช้reject()ข้อความใด ๆในสัญญาของคุณ อย่างไรก็ตามเพื่อให้แน่ใจว่าแอพ / สคริปต์ของคุณไม่หยุดในกรณีที่สัญญาพื้นฐานเดียวไม่เคยได้รับการตอบกลับคุณจะต้องหมดเวลาใช้งาน

function getThing(uid,branch){
    return new Promise(function (resolve, reject) {
        xhr.get().then(function(res) {
            if (res) {
                resolve(res);
            } 
            else {
                resolve(null);
            }
            setTimeout(function(){reject('timeout')},10000)
        }).catch(function(error) {
            resolve(null);
        });
    });
}

นี่คือคำตอบ: stackoverflow.com/questions/31424561/ …
Humoyun Ahmad

การไม่ใช้reject()สัญญาของคุณเป็นเรื่องปกติ แต่ถ้าคุณจำเป็นต้องใช้สัญญาห้องสมุดอื่น
Dan Dascalescu

-8

ฉันเขียนไลบรารี npm เพื่อจัดการกับปัญหานี้สวยงามยิ่งขึ้น https://github.com/wenshin/promiseallend

ติดตั้ง

npm i --save promiseallend

2017-02-25 API ใหม่มันไม่ทำลายหลักการสัญญา

const promiseAllEnd = require('promiseallend');

const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};

// input promises with array
promiseAllEnd(promises, {
    unhandledRejection(error, index) {
        // error is the original error which is 'error'.
        // index is the index of array, it's a number.
        console.log(error, index);
    }
})
    // will call, data is `[1, undefined, 2]`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// input promises with object
promiseAllEnd(promisesObj, {
    unhandledRejection(error, prop) {
        // error is the original error.
        // key is the property of object.
        console.log(error, prop);
    }
})
    // will call, data is `{k1: 1, k3: 2}`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
    // won't call
    .then(data => console.log(data))
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
    // will call, data is `[1, undefined, 2]`.
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

--------------------------------

API เก่าที่ไม่ดีอย่าใช้มัน!

let promiseAllEnd = require('promiseallend');

// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
    .then(data => console.log(data)) // [1, undefined, 2]
    .catch(error => console.log(error.errorsByKey)) // {1: 'error'}

// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
    .then(data => console.log(data)) // {k1: 1, k3: 2}
    .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}

มันทำงานยังไง? กรุณาแสดงและอธิบายการใช้งานฟังก์ชั่นของคุณ
Bergi

Promise.allผมเขียนตรรกะพร้อมกันใหม่เช่น แต่มันจะรวบรวมข้อมูลทั้งหมดและข้อผิดพลาดของสัญญาทั้งหมด นอกจากนี้ยังรองรับอินพุตวัตถุก็ไม่ได้ชี้ หลังจากรวบรวมข้อมูลและข้อผิดพลาดทั้งหมดฉันจะแทนที่promise.thenวิธีการจัดการกับการเรียกกลับที่ลงทะเบียนซึ่งรวมถึงการปฏิเสธและการเติมเต็ม สำหรับรายละเอียดคุณสามารถดูรหัส
wenshin

เอ่อรหัสนั้นจะเรียกทั้งสองonFulfilledและonRejectedตัวจัดการที่ถูกส่งไปยังthen?
Bergi

ใช่เฉพาะเมื่อผสมสถานะสัญญาและfulfilled rejectedแต่จริงๆมันก่อให้เกิดปัญหาที่ยากที่จะเข้ากันได้กับกรณีการใช้สัญญาทั้งหมดได้ตามปกติเหมือนonFulfilledและonRejectedผลตอบแทนทั้งหมดหรือPromise.reject() Promise.resolve()จนถึงตอนนี้ฉันยังไม่ชัดเจนว่าจะแก้ไขได้อย่างไรมีใครมีความคิดที่ดีกว่านี้บ้างไหม? คำตอบที่ดีที่สุดสำหรับตอนนี้มีปัญหาคือมันอาจไม่สามารถกรองข้อมูลและข้อผิดพลาดในสภาพแวดล้อมของเบราว์เซอร์
wenshin

เราจำเป็นต้องติดตั้งโมดูล npm ด้วย pip python package manager หรือไม่
sevenfourk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.