ทำลายโซ่สัญญาและเรียกใช้ฟังก์ชันตามขั้นตอนในห่วงโซ่ที่ขาด (ปฏิเสธ)


140

อัปเดต:

เพื่อช่วยผู้ชมโพสต์นี้ในอนาคตฉันได้สร้างตัวอย่างคำตอบของ plumaนี้

คำถาม:

เป้าหมายของฉันค่อนข้างตรงไปตรงมา

  step(1)
  .then(function() {
    return step(2);
  }, function() {
    stepError(1);
    return $q.reject();
  })
  .then(function() {

  }, function() {
    stepError(2);
  });

  function step(n) {
    var deferred = $q.defer();
    //fail on step 1
    (n === 1) ? deferred.reject() : deferred.resolve();
    return deferred.promise;
  }
  function stepError(n) {
    console.log(n); 
  }

ปัญหาคือถ้าฉันล้มเหลวในขั้นตอนที่ 1 ทั้งstepError(1)AND stepError(2)จะถูกไล่ออก ถ้าฉันไม่ทำreturn $q.rejectก็stepError(2)จะไม่ถูกไล่ออก แต่step(2)จะซึ่งฉันเข้าใจ ฉันทำทุกอย่างสำเร็จยกเว้นสิ่งที่ฉันพยายามทำ

ฉันจะเขียนคำสัญญาเพื่อเรียกใช้ฟังก์ชันเมื่อปฏิเสธโดยไม่เรียกฟังก์ชันทั้งหมดในห่วงโซ่ข้อผิดพลาดได้อย่างไร หรือมีวิธีอื่นที่จะทำสิ่งนี้ให้สำเร็จ?

นี่คือการสาธิตสดเพื่อให้คุณมีบางอย่างที่ใช้ได้ผล

อัปเดต:

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

สาธิตสดที่นี่ (คลิก)

step(1)
  .then(function() {
    return step(2);
  })
  .then(function() {
    return step(3);
  })
  .then(false, 
    function(x) {
      stepError(x);
    }
  );
  function step(n) {
    console.log('Step '+n);
    var deferred = $q.defer();
    (n === 1) ? deferred.reject(n) : deferred.resolve(n);
    return deferred.promise;
  }
  function stepError(n) {
    console.log('Error '+n); 
  }

1
มี async javascript lib ที่อาจช่วยได้หากสิ่งนี้ซับซ้อนขึ้น
lucuma

Promise.prototype.catch()ตัวอย่างบน MDNแสดงวิธีแก้ปัญหาสำหรับปัญหาเดียวกัน
toraritte

คำตอบ:


203

สาเหตุที่รหัสของคุณไม่ทำงานตามที่คาดไว้คือจริงๆแล้วมันทำอะไรบางอย่างที่แตกต่างจากที่คุณคิด

สมมติว่าคุณมีสิ่งต่อไปนี้:

stepOne()
.then(stepTwo, handleErrorOne)
.then(stepThree, handleErrorTwo)
.then(null, handleErrorThree);

เพื่อให้เข้าใจสิ่งที่เกิดขึ้นได้ดีขึ้นลองแกล้งทำเป็นรหัสซิงโครนัสกับtry/ catchบล็อก:

try {
    try {
        try {
            var a = stepOne();
        } catch(e1) {
            a = handleErrorOne(e1);
        }
        var b = stepTwo(a);
    } catch(e2) {
        b = handleErrorTwo(e2);
    }
    var c = stepThree(b);
} catch(e3) {
    c = handleErrorThree(e3);
}

onRejectedจัดการ (อาร์กิวเมนต์ที่สองของthen) เป็นหลักกลไกการแก้ไขข้อผิดพลาด (เช่นcatchบล็อก) หากมีข้อผิดพลาดเกิดขึ้นข้อผิดพลาดhandleErrorOneจะถูกจับโดยบล็อกจับถัดไป ( catch(e2)) และอื่น ๆ

เห็นได้ชัดว่านี่ไม่ใช่สิ่งที่คุณตั้งใจไว้

สมมติว่าเราต้องการให้ห่วงโซ่ความละเอียดทั้งหมดล้มเหลวไม่ว่าจะเกิดอะไรขึ้น:

stepOne()
.then(function(a) {
    return stepTwo(a).then(null, handleErrorTwo);
}, handleErrorOne)
.then(function(b) {
    return stepThree(b).then(null, handleErrorThree);
});

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

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

เหตุผลนี้ได้ผลคือทั้งสองonFulfilledและonRejectedเป็นอาร์กิวเมนต์เสริมสำหรับthenวิธีการ หากสัญญาสำเร็จ (กล่าวคือได้รับการแก้ไข) และถัดไปthenในห่วงโซ่ไม่มีonFulfilledตัวจัดการโซ่จะดำเนินต่อไปจนกว่าจะมีตัวจัดการดังกล่าว

ซึ่งหมายความว่าสองบรรทัดต่อไปนี้เทียบเท่า:

stepOne().then(stepTwo, handleErrorOne)
stepOne().then(null, handleErrorOne).then(stepTwo)

แต่บรรทัดต่อไปนี้ไม่เทียบเท่ากับสองข้อด้านบน:

stepOne().then(stepTwo).then(null, handleErrorOne)

ไลบรารีคำสัญญาของ Angular $qขึ้นอยู่กับQไลบรารีของ kriskowal (ซึ่งมี API ที่สมบูรณ์กว่า แต่มีทุกสิ่งที่คุณสามารถค้นหาได้$q) เอกสาร APIของ Q บน GitHub สามารถพิสูจน์ได้ว่ามีประโยชน์ Q ใช้ข้อมูลจำเพาะของสัญญา / A +ซึ่งจะกล่าวถึงรายละเอียดthenว่าพฤติกรรมการแก้ปัญหาสัญญาทำงานอย่างไรและอย่างไร

แก้ไข:

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

ซึ่งหมายความว่าหากคุณไม่ส่งคืนอะไรเลยแสดงว่าคุณกำลังส่งคืนสัญญาที่ได้รับการแก้ไขแล้วสำหรับมูลค่าundefinedนั้น


140
ส่วนนี้เป็นสีทอง: if you don't return anything, you are effectively returning a resolved promise for the value undefined.ขอบคุณ @pluma
Valerio

8
นี่คือแน่นอน ฉันกำลังแก้ไขเพื่อให้มันเป็นตัวหนาที่สมควรได้รับ
Cyril CHAPON

ปฏิเสธออกจากฟังก์ชันปัจจุบันหรือไม่ เช่นการแก้ปัญหาจะไม่ถูกเรียกถ้าการปฏิเสธเรียกว่า 1st `if (ไม่ดี) {ปฏิเสธ (สถานะ); } แก้ไข (ผลลัพธ์); `
SuperUberDuper

stepOne().then(stepTwo, handleErrorOne) `stepOne () แล้ว (null, handleErrorOne) จากนั้น (stepTwo)` สิ่งเหล่านี้เทียบเท่ากับ trully หรือไม่? ฉันคิดว่าในกรณีของการปฏิเสธในstepOneบรรทัดที่สองของโค้ดจะดำเนินการstepTwoแต่อันแรกจะดำเนินการhandleErrorOneและหยุดเท่านั้น หรือฉันขาดอะไรไป?
JeFf

5
ไม่ได้ให้คำตอบที่ชัดเจนสำหรับคำถามที่ถาม แต่คำอธิบายที่ดีอย่างไรก็ตาม
Yerken

59

ไปงานปาร์ตี้ช้าไปหน่อย แต่วิธีง่ายๆนี้ใช้ได้ผลสำหรับฉัน:

function chainError(err) {
  return Promise.reject(err)
};

stepOne()
.then(stepTwo, chainError)
.then(stepThreee, chainError);

วิธีนี้ช่วยให้คุณหลุดออกจากโซ่ได้


2
ช่วยฉัน แต่ FYI คุณสามารถส่งคืนได้ในตอนนั้นเพื่อแยกออกในการจับเช่น:.then(user => { if (user) return Promise.reject('The email address already exists.') })
Craig van Tonder

1
@CraigvanTonder คุณสามารถโยนสัญญาและมันจะทำงานเหมือนกับรหัสของคุณ:.then(user => { if (user) throw 'The email address already exists.' })
Francisco Presencia

1
นี่เป็นคำตอบเดียวที่ถูกต้อง มิฉะนั้นขั้นตอนที่ 3 จะยังคงดำเนินการแม้ขั้นตอนที่ 1 จะมีข้อผิดพลาด
wdetac

1
เพียงเพื่อชี้แจงหากมีข้อผิดพลาดเกิดขึ้นใน stepOne () ดังนั้นทั้ง chainError จะถูกเรียกใช้ใช่ไหม หากถูกใจสิ่งนี้ ฉันมีตัวอย่างข้อมูลที่ทำสิ่งนี้ไม่แน่ใจว่าฉันเข้าใจผิดหรือไม่ - runkit.com/embed/9q2q3rjxdar9
user320550

10

สิ่งที่คุณต้องการคือ.then()โซ่ซ้ำพร้อมกรณีพิเศษในการเริ่มต้นและกรณีพิเศษที่จะเสร็จสิ้น

ความสามารถพิเศษคือการรับหมายเลขขั้นตอนของกรณีความล้มเหลวเพื่อกระเพื่อมไปยังตัวจัดการข้อผิดพลาดขั้นสุดท้าย

  • เริ่มต้น: โทรstep(1)โดยไม่มีเงื่อนไข
  • รูปแบบการทำซ้ำ: เชื่อมต่อ.then()กับการเรียกกลับต่อไปนี้:
    • ความสำเร็จ: ขั้นตอนการโทร (n + 1)
    • ความล้มเหลว: โยนค่าที่การรอการตัดบัญชีก่อนหน้านี้ถูกปฏิเสธหรือสร้างข้อผิดพลาดอีกครั้ง
  • เสร็จสิ้น: เชื่อมต่อ.then()โดยไม่มีตัวจัดการความสำเร็จและตัวจัดการข้อผิดพลาดขั้นสุดท้าย

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

function nextStep(n) {
    return step(n + 1);
}

function step(n) {
    console.log('step ' + n);
    var deferred = $q.defer();
    (n === 3) ? deferred.reject(n) : deferred.resolve(n);
    return deferred.promise;
}

function stepError(n) {
    throw(n);
}

function finalError(n) {
    console.log('finalError ' + n);
}
step(1)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(nextStep, stepError)
    .then(null, finalError);});

ดูการสาธิต

สังเกตว่าในstep()การเลื่อนเวลาถูกปฏิเสธหรือแก้ไขด้วยวิธีnใดจึงทำให้ค่านั้นพร้อมใช้งานสำหรับการเรียกกลับในลำดับถัดไป.then()ในห่วงโซ่ เมื่อstepErrorเรียกว่าข้อผิดพลาดที่ rethrown ซ้ำ ๆ finalErrorจนกว่าจะมีการจัดการโดย


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

1
m59 นี่คือคำตอบสำหรับคำถามที่ถามว่า "ฉันจะเขียนสัญญาอย่างไรเพื่อให้ฉันสามารถเรียกใช้ฟังก์ชันเมื่อปฏิเสธโดยไม่เรียกฟังก์ชันทั้งหมดในห่วงโซ่ข้อผิดพลาด" และชื่อคำถาม "ทำลายโซ่สัญญาและเรียกใช้ฟังก์ชันตามขั้นตอนในห่วงโซ่ที่ขาด (ปฏิเสธ)"
Beetroot-Beetroot

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

7

เมื่อปฏิเสธคุณควรผ่านข้อผิดพลาดในการปฏิเสธจากนั้นรวมตัวจัดการข้อผิดพลาดขั้นตอนในฟังก์ชันที่ตรวจสอบว่าควรดำเนินการปฏิเสธหรือ "เปลี่ยนใหม่" จนกว่าจะสิ้นสุดห่วงโซ่:

// function mocking steps
function step(i) {
    i++;
    console.log('step', i);
    return q.resolve(i);
}

// function mocking a failing step
function failingStep(i) {
    i++;
    console.log('step '+ i + ' (will fail)');
    var e = new Error('Failed on step ' + i);
    e.step = i;
    return q.reject(e);
}

// error handler
function handleError(e){
    if (error.breakChain) {
        // handleError has already been called on this error
        // (see code bellow)
        log('errorHandler: skip handling');
        return q.reject(error);
    }
    // firs time this error is past to the handler
    console.error('errorHandler: caught error ' + error.message);
    // process the error 
    // ...
    //
    error.breakChain = true;
    return q.reject(error);
}

// run the steps, will fail on step 4
// and not run step 5 and 6
// note that handleError of step 5 will be called
// but since we use that error.breakChain boolean
// no processing will happen and the error will
// continue through the rejection path until done(,)

  step(0) // 1
  .catch(handleError)
  .then(step) // 2
  .catch(handleError)
  .then(step) // 3
  .catch(handleError)
  .then(failingStep)  // 4 fail
  .catch(handleError)
  .then(step) // 5
  .catch(handleError)
  .then(step) // 6
  .catch(handleError)
  .done(function(){
      log('success arguments', arguments);
  }, function (error) {
      log('Done, chain broke at step ' + error.step);
  });

สิ่งที่คุณเห็นบนคอนโซล:

step 1
step 2
step 3
step 4 (will fail)
errorHandler: caught error 'Failed on step 4'
errorHandler: skip handling
errorHandler: skip handling
Done, chain broke at step 4

นี่คือรหัสการทำงานบางส่วน https://jsfiddle.net/8hzg5s7m/3/

หากคุณมีการจัดการที่เฉพาะเจาะจงสำหรับแต่ละขั้นตอน Wrapper ของคุณอาจเป็นดังนี้:

/*
 * simple wrapper to check if rejection
 * has already been handled
 * @param function real error handler
 */
function createHandler(realHandler) {
    return function(error) {
        if (error.breakChain) {
            return q.reject(error);
        }
        realHandler(error);
        error.breakChain = true;
        return q.reject(error);    
    }
}

จากนั้นโซ่ของคุณ

step1()
.catch(createHandler(handleError1Fn))
.then(step2)
.catch(createHandler(handleError2Fn))
.then(step3)
.catch(createHandler(handleError3Fn))
.done(function(){
    log('success');
}, function (error) {
    log('Done, chain broke at step ' + error.step);
});

2

ถ้าฉันเข้าใจถูกต้องคุณต้องการให้แสดงเฉพาะข้อผิดพลาดของขั้นตอนที่ล้มเหลวใช่ไหม

นั่นควรจะง่ายพอ ๆ กับการเปลี่ยนกรณีความล้มเหลวของสัญญาแรกเป็นสิ่งนี้:

step(1).then(function (response) {
    step(2);
}, function (response) {
    stepError(1);
    return response;
}).then( ... )

โดยการกลับ$q.reject()ในกรณีความล้มเหลวในขั้นตอนแรกของคุณจะปฏิเสธสัญญาซึ่งเป็นสาเหตุ errorCallback then(...)ที่จะเรียกว่าในปีที่ 2


อะไรในโลก ... นั่นคือสิ่งที่ฉันทำ! ดูในโพสต์ของฉันที่ฉันไม่พยายามที่ step(2)แต่ห่วงโซ่จะเตะกลับมาอยู่ในและเรียกใช้ ตอนนี้ฉันเพิ่งลองอีกครั้งมันไม่เกิดขึ้น ผมงงไปหมดแล้ว.
m59

1
ฉันเห็นว่าคุณพูดถึงเรื่องนั้น นั่นเป็นเรื่องแปลกประหลาด ฟังก์ชั่นที่มีreturn step(2);ควรถูกเรียกใช้เมื่อstep(1)แก้ไขสำเร็จแล้วเท่านั้น
Zajn

เกานั่น - มันกำลังเกิดขึ้นแน่นอน อย่างที่บอกไว้ในโพสต์ของฉันถ้าคุณไม่ใช้return $q.reject()โซ่ก็จะดำเนินต่อไป ในกรณีนี้ทำให้return responseสับสน ดูสิ่งนี้: jsbin.com/EpaZIsIp/6/edit
m59

อืมโอเค ดูเหมือนว่าจะใช้งานได้ใน jsbin ที่คุณโพสต์เมื่อฉันเปลี่ยนสิ่งนั้น แต่ฉันต้องพลาดอะไรบางอย่าง
Zajn

ใช่ฉันเห็นว่าตอนนี้ใช้ไม่ได้แล้ว กลับไปที่กระดานวาดภาพสำหรับฉัน!
Zajn

2
var s = 1;
start()
.then(function(){
    return step(s++);
})
.then(function() {
    return step(s++);
})
.then(function() {
    return step(s++);
})
.then(0, function(e){
   console.log(s-1); 
});

http://jsbin.com/EpaZIsIp/20/edit

หรืออัตโนมัติสำหรับหลายขั้นตอน:

var promise = start();
var s = 1;
var l = 3;
while(l--) {
    promise = promise.then(function() {
        return step(s++);
    });
}
promise.then(0, function(e){
   console.log(s-1); 
});

http://jsbin.com/EpaZIsIp/21/edit


แต่ถ้าฉันจะโทรdeferred.reject(n)ไปฉันก็ได้รับคำเตือนว่าสัญญาถูกปฏิเสธด้วยวัตถุ
nonError


2

หากคุณต้องการแก้ปัญหานี้โดยใช้ async / await:

(async function(){    
    try {        
        const response1, response2, response3
        response1 = await promise1()

        if(response1){
            response2 = await promise2()
        }
        if(response2){
            response3 = await promise3()
        }
        return [response1, response2, response3]
    } catch (error) {
        return []
    }

})()

1

แนบตัวจัดการข้อผิดพลาดเป็นองค์ประกอบลูกโซ่แยกโดยตรงกับการดำเนินการตามขั้นตอน:

        // Handle errors for step(1)
step(1).then(null, function() { stepError(1); return $q.reject(); })
.then(function() {
                 // Attach error handler for step(2),
                 // but only if step(2) is actually executed
  return step(2).then(null, function() { stepError(2); return $q.reject(); });
})
.then(function() {
                 // Attach error handler for step(3),
                 // but only if step(3) is actually executed
  return step(3).then(null, function() { stepError(3); return $q.reject(); });
});

หรือใช้catch():

       // Handle errors for step(1)
step(1).catch(function() { stepError(1); return $q.reject(); })
.then(function() {
                 // Attach error handler for step(2),
                 // but only if step(2) is actually executed
  return step(2).catch(function() { stepError(2); return $q.reject(); });
})
.then(function() {
                 // Attach error handler for step(3),
                 // but only if step(3) is actually executed
  return step(3).catch(function() { stepError(3); return $q.reject(); });
});

หมายเหตุ: นี่เป็นรูปแบบเดียวกับที่pluma แนะนำในคำตอบของเขาแต่ใช้การตั้งชื่อของ OP


1

พบPromise.prototype.catch()ตัวอย่างใน MDNด้านล่างที่มีประโยชน์มาก

(คำตอบที่ได้รับการยอมรับกล่าวถึงthen(null, onErrorHandler)ซึ่งโดยพื้นฐานแล้วจะเหมือนกับcatch(onErrorHandler))

การใช้และการผูกมัดวิธีการจับ

var p1 = new Promise(function(resolve, reject) {
  resolve('Success');
});

p1.then(function(value) {
  console.log(value); // "Success!"
  throw 'oh, no!';
}).catch(function(e) {
  console.log(e); // "oh, no!"
}).then(function(){
  console.log('after a catch the chain is restored');
}, function () {
  console.log('Not fired due to the catch');
});

// The following behaves the same as above
p1.then(function(value) {
  console.log(value); // "Success!"
  return Promise.reject('oh, no!');
}).catch(function(e) {
  console.log(e); // "oh, no!"
}).then(function(){
  console.log('after a catch the chain is restored');
}, function () {
  console.log('Not fired due to the catch');
});

Gotchas เมื่อโยนข้อผิดพลาด

// Throwing an error will call the catch method most of the time
var p1 = new Promise(function(resolve, reject) {
  throw 'Uh-oh!';
});

p1.catch(function(e) {
  console.log(e); // "Uh-oh!"
});

// Errors thrown inside asynchronous functions will act like uncaught errors
var p2 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    throw 'Uncaught Exception!';
  }, 1000);
});

p2.catch(function(e) {
  console.log(e); // This is never called
});

// Errors thrown after resolve is called will be silenced
var p3 = new Promise(function(resolve, reject) {
  resolve();
  throw 'Silenced Exception!';
});

p3.catch(function(e) {
   console.log(e); // This is never called
});

หากได้รับการแก้ไข

//Create a promise which would not call onReject
var p1 = Promise.resolve("calling next");

var p2 = p1.catch(function (reason) {
    //This is never called
    console.log("catch p1!");
    console.log(reason);
});

p2.then(function (value) {
    console.log("next promise's onFulfilled"); /* next promise's onFulfilled */
    console.log(value); /* calling next */
}, function (reason) {
    console.log("next promise's onRejected");
    console.log(reason);
});

1

ทางออกที่ดีที่สุดคือการปรับโครงสร้างให้เข้ากับห่วงโซ่สัญญาของคุณเพื่อใช้ ES6 ที่รอคอย จากนั้นคุณสามารถกลับจากฟังก์ชั่นเพื่อข้ามพฤติกรรมที่เหลือได้

ฉันตีหัวตัวเองกับรูปแบบนี้มานานกว่าหนึ่งปีแล้วและการใช้การรอคอยคือสวรรค์


เมื่อใช้ IE async / await ไม่รองรับ
ndee

0

หากเมื่อใดก็ตามที่คุณกลับมาPromise.reject('something')คุณจะถูกโยนเข้าไปในด่านจับตามคำสัญญา

promiseOne
  .then((result) => {
    if (!result) {
      return Promise.reject('No result');
    }
    return;
  })
  .catch((err) => {
    console.log(err);
  });

หากคำสัญญาแรกไม่ส่งกลับผลลัพธ์ใด ๆ คุณจะได้รับ'ไม่มีผลลัพธ์'ในคอนโซลเท่านั้น


-1

ใช้โมดูล SequentialPromise

ความตั้งใจ

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

ผู้เข้าร่วม

  • บริบท : อ็อบเจ็กต์ที่เมธอดสมาชิกดำเนินการ
  • SequentialPromise : กำหนดexecuteวิธีการเชื่อมโยงและติดตามแต่ละการดำเนินการ SequentialPromise ส่งคืน Promise-Chain จากการดำเนินการทั้งหมดที่ดำเนินการ
  • Invoker : สร้างอินสแตนซ์ SequentialPromise จัดเตรียมบริบทและการดำเนินการและเรียกexecuteใช้เมธอดในขณะที่ส่งรายการลำดับตัวเลือกสำหรับแต่ละการดำเนินการ

ผลที่ตามมา

ใช้ SequentialPromise เมื่อจำเป็นต้องมีพฤติกรรมตามลำดับของการแก้ปัญหาตามสัญญา SequentialPromise จะติดตามดัชนีที่ Promise ถูกปฏิเสธ

การนำไปใช้

clear();

var http = {
    get(url) {
        var delay = Math.floor( Math.random() * 10 ), even = !(delay % 2);
        var xhr = new Promise(exe);

        console.log(`REQUEST`, url, delay);
        xhr.then( (data) => console.log(`SUCCESS: `, data) ).catch( (data) => console.log(`FAILURE: `, data) );

        function exe(resolve, reject) {
            var action = { 'true': reject, 'false': resolve }[ even ];
            setTimeout( () => action({ url, delay }), (1000 * delay) );
        }

        return xhr;
    }
};

var SequentialPromise = new (function SequentialPromise() {
    var PRIVATE = this;

    return class SequentialPromise {

        constructor(context, action) {
            this.index = 0;
            this.requests = [ ];
            this.context = context;
            this.action = action;

            return this;
        }

        log() {}

        execute(url, ...more) {
            var { context, action, requests } = this;
            var chain = context[action](url);

            requests.push(chain);
            chain.then( (data) => this.index += 1 );

            if (more.length) return chain.then( () => this.execute(...more) );
            return chain;
        }

    };
})();

var sequence = new SequentialPromise(http, 'get');
var urls = [
    'url/name/space/0',
    'url/name/space/1',
    'url/name/space/2',
    'url/name/space/3',
    'url/name/space/4',
    'url/name/space/5',
    'url/name/space/6',
    'url/name/space/7',
    'url/name/space/8',
    'url/name/space/9'
];
var chain = sequence.execute(...urls);
var promises = sequence.requests;

chain.catch( () => console.warn(`EXECUTION STOPPED at ${sequence.index} for ${urls[sequence.index]}`) );

// console.log('>', chain, promises);

ส่วนสำคัญ

SequentialPromise

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