ยกเลิกโซ่สัญญาวานิลลา ECMAScript 6


111

มีวิธีการล้างอินสแตนซ์.thenJavaScript Promiseหรือไม่?

ผมเคยเขียนกรอบการทดสอบ JavaScript บนด้านบนของQUnit เฟรมเวิร์กจะทำการทดสอบพร้อมกันโดยรันแต่ละอันในไฟล์Promise. (ขออภัยสำหรับความยาวของบล็อกโค้ดนี้ฉันแสดงความคิดเห็นว่าดีที่สุดเท่าที่จะทำได้ดังนั้นจึงรู้สึกน่าเบื่อน้อยลง)

/* Promise extension -- used for easily making an async step with a
       timeout without the Promise knowing anything about the function 
       it's waiting on */
$$.extend(Promise, {
    asyncTimeout: function (timeToLive, errorMessage) {
        var error = new Error(errorMessage || "Operation timed out.");
        var res, // resolve()
            rej, // reject()
            t,   // timeout instance
            rst, // reset timeout function
            p,   // the promise instance
            at;  // the returned asyncTimeout instance

        function createTimeout(reject, tempTtl) {
            return setTimeout(function () {
                // triggers a timeout event on the asyncTimeout object so that,
                // if we want, we can do stuff outside of a .catch() block
                // (may not be needed?)
                $$(at).trigger("timeout");

                reject(error);
            }, tempTtl || timeToLive);
        }

        p = new Promise(function (resolve, reject) {
            if (timeToLive != -1) {
                t = createTimeout(reject);

                // reset function -- allows a one-time timeout different
                //    from the one original specified
                rst = function (tempTtl) {
                    clearTimeout(t);
                    t = createTimeout(reject, tempTtl);
                }
            } else {
                // timeToLive = -1 -- allow this promise to run indefinitely
                // used while debugging
                t = 0;
                rst = function () { return; };
            }

            res = function () {
                clearTimeout(t);
                resolve();
            };

            rej = reject;
        });

        return at = {
            promise: p,
            resolve: res,
            reject: rej,
            reset: rst,
            timeout: t
        };
    }
});

/* framework module members... */

test: function (name, fn, options) {
    var mod = this; // local reference to framework module since promises
                    // run code under the window object

    var defaultOptions = {
        // default max running time is 5 seconds
        timeout: 5000
    }

    options = $$.extend({}, defaultOptions, options);

    // remove timeout when debugging is enabled
    options.timeout = mod.debugging ? -1 : options.timeout;

    // call to QUnit.test()
    test(name, function (assert) {
        // tell QUnit this is an async test so it doesn't run other tests
        // until done() is called
        var done = assert.async();
        return new Promise(function (resolve, reject) {
            console.log("Beginning: " + name);

            var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
            $$(at).one("timeout", function () {
                // assert.fail() is just an extension I made that literally calls
                // assert.ok(false, msg);
                assert.fail("Test timed out");
            });

            // run test function
            var result = fn.call(mod, assert, at.reset);

            // if the test returns a Promise, resolve it before resolving the test promise
            if (result && result.constructor === Promise) {
                // catch unhandled errors thrown by the test so future tests will run
                result.catch(function (error) {
                    var msg = "Unhandled error occurred."
                    if (error) {
                        msg = error.message + "\n" + error.stack;
                    }

                    assert.fail(msg);
                }).then(function () {
                    // resolve the timeout Promise
                    at.resolve();
                    resolve();
                });
            } else {
                // if test does not return a Promise, simply clear the timeout
                // and resolve our test Promise
                at.resolve();
                resolve();
            }
        }).then(function () {
            // tell QUnit that the test is over so that it can clean up and start the next test
            done();
            console.log("Ending: " + name);
        });
    });
}

หากการทดสอบหมดเวลาสัญญาหมดเวลาของฉันจะassert.fail()ทำการทดสอบเพื่อให้การทดสอบถูกทำเครื่องหมายว่าล้มเหลวซึ่งทั้งหมดนั้นดีและดี แต่การทดสอบยังคงดำเนินต่อไปเนื่องจากการทดสอบ Promise ( result) ยังคงรอการแก้ไขอยู่

ฉันต้องการวิธีที่ดีในการยกเลิกการทดสอบของฉัน ฉันสามารถทำได้โดยการสร้างฟิลด์ในโมดูลเฟรมเวิร์กthis.cancelTestหรือบางสิ่งบางอย่างและตรวจสอบทุก ๆ ครั้ง (เช่นที่จุดเริ่มต้นของthen()การทำซ้ำแต่ละครั้ง) ภายในการทดสอบว่าจะยกเลิกหรือไม่ อย่างไรก็ตาม$$(at).on("timeout", /* something here */)ตามหลักการแล้วฉันสามารถใช้เพื่อล้างค่าthen()s ที่เหลือบนresultตัวแปรของฉันเพื่อไม่ให้การทดสอบที่เหลือถูกเรียกใช้

สิ่งนี้มีอยู่จริงหรือไม่?

อัปเดตด่วน

ฉันลองใช้Promise.race([result, at.promise]). มันไม่ได้ผล

อัปเดต 2+ ความสับสน

ในการปลดบล็อกฉันฉันได้เพิ่มสองสามบรรทัดด้วยmod.cancelTest/ การสำรวจความคิดเห็นภายในแนวคิดการทดสอบ (ฉันลบทริกเกอร์เหตุการณ์ด้วย)

return new Promise(function (resolve, reject) {
    console.log("Beginning: " + name);

    var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
    at.promise.catch(function () {
        // end the test if it times out
        mod.cancelTest = true;
        assert.fail("Test timed out");
        resolve();
    });

    // ...
    
}).then(function () {
    // tell QUnit that the test is over so that it can clean up and start the next test
    done();
    console.log("Ending: " + name);
});

ฉันตั้งค่าเบรกพอยต์ในcatchคำสั่งและมันถูกโจมตี สิ่งที่ทำให้ฉันสับสนตอนนี้then()คือไม่มีการเรียกใบแจ้งยอด ไอเดีย?

อัปเดต 3

คิดสิ่งสุดท้ายออก fn.call()กำลังแสดงข้อผิดพลาดซึ่งฉันไม่ได้จับดังนั้นคำสัญญาในการทดสอบจึงถูกปฏิเสธก่อนที่at.promise.catch()จะแก้ไขได้


เป็นไปได้ที่จะทำการยกเลิกด้วยสัญญา ES6 แต่ไม่ใช่คุณสมบัติของสัญญา (แต่เป็นคุณสมบัติของฟังก์ชันที่คืนค่า) ฉันสามารถสร้างตัวอย่างสั้น ๆ ได้หากคุณสนใจ
Benjamin Gruenbaum

@BenjaminGruenbaum ฉันรู้ว่าเกือบปีแล้ว แต่ฉันก็ยังสนใจถ้าคุณมีเวลาเขียนตัวอย่าง :)
dx_over_dt

1
เป็นปีที่ผ่านมา แต่มีการพูดคุยอย่างเป็นทางการเมื่อสองวันก่อนเมื่อวานนี้โดยมีโทเค็นการยกเลิกและสัญญาที่ยกเลิกได้จะย้ายไปที่ขั้นที่ 1
Benjamin Gruenbaum

3
คำตอบ ES6 สำหรับการยกเลิกสัญญาเป็นสิ่งที่สังเกตได้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่นี่: github.com/Reactive-Extensions/RxJS
Frank Goortani

เชื่อมโยงคำตอบของฉันเกี่ยวกับการใช้Prexห้องสมุดเพื่อยกเลิกสัญญา
noseratio

คำตอบ:


76

มีวิธีการล้าง.thenอินสแตนซ์ JavaScript Promise หรือไม่?

ไม่ไม่อยู่ใน ECMAScript 6 เป็นอย่างน้อย สัญญา (ของพวกเขาและthenรถยก) จะไม่สามารถยกเลิกโดยปริยาย(ขออภัย) มีการพูดคุยกันเล็กน้อยเกี่ยวกับ es-Discuss (เช่นที่นี่ ) เกี่ยวกับวิธีการทำสิ่งนี้อย่างถูกต้อง แต่วิธีใดก็ตามที่จะชนะมันจะไม่เข้าสู่ ES6

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

จนกว่าคณะกรรมการภาษาจะหาวิธีที่ดีที่สุด(หวังว่า ES7?)คุณยังคงสามารถใช้การใช้งาน Userland Promise ซึ่งหลาย ๆ ฟีเจอร์จะยกเลิก

การสนทนาปัจจุบันอยู่ในhttps://github.com/domenic/cancelable-promiseและhttps://github.com/bergus/promise-cancellationแบบร่าง


2
"การสนทนาเล็กน้อย" - ฉันสามารถเชื่อมโยงไปยัง 30 เธรดใน esdiscuss หรือ GitHub :) (ไม่ต้องพูดถึงความช่วยเหลือของคุณเองในการยกเลิกใน bluebird 3.0)
Benjamin Gruenbaum

@BenjaminGruenbaum: คุณมีลิงค์ที่พร้อมจะแชร์ที่ไหนสักแห่งไหม? ฉันต้องการสรุปความคิดเห็นและความพยายามมานานแล้วและโพสต์ข้อเสนอเพื่อ esdiscuss ดังนั้นฉันจึงยินดีหากสามารถตรวจสอบย้อนกลับได้ว่าฉันลืมอะไรไป
Bergi

ฉันมีประโยชน์ในการทำงาน - ดังนั้นฉันจะมีให้ใน 3-4 วัน คุณสามารถตรวจสอบข้อกำหนดการยกเลิกสัญญาภายใต้สัญญา - เสริมเพื่อการเริ่มต้นที่ดี
Benjamin Gruenbaum

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

6
Domenic ลบข้อเสนอ TC39 ... ... cc @BenjaminGruenbaum
Sergio

50

แม้ว่าจะไม่มีวิธีมาตรฐานในการทำสิ่งนี้ใน ES6 แต่ก็มีไลบรารีชื่อBluebirdเพื่อจัดการสิ่งนี้

นอกจากนี้ยังมีวิธีที่แนะนำซึ่งอธิบายไว้เป็นส่วนหนึ่งของเอกสารการตอบสนอง ดูเหมือนว่าคุณมีในการอัปเดต 2 และ 3

const makeCancelable = (promise) => {
  let hasCanceled_ = false;

  const wrappedPromise = new Promise((resolve, reject) => {
    promise.then((val) =>
      hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    );
    promise.catch((error) =>
      hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled_ = true;
    },
  };
};

const cancelablePromise = makeCancelable(
  new Promise(r => component.setState({...}}))
);

cancelablePromise
  .promise
  .then(() => console.log('resolved'))
  .catch((reason) => console.log('isCanceled', reason.isCanceled));

cancelablePromise.cancel(); // Cancel the promise

นำมาจาก: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html


1
คำจำกัดความของการยกเลิกนี้เป็นเพียงการปฏิเสธคำสัญญา ขึ้นอยู่กับคำจำกัดความของ "ยกเลิก"
Alexander Mills

1
และจะเกิดอะไรขึ้นหากคุณต้องการยกเลิกสัญญาชุดหนึ่ง?
Matthieu Brucher

1
ปัญหาของแนวทางนี้คือหากคุณมีคำมั่นสัญญาที่จะไม่แก้ไขหรือปฏิเสธก็จะไม่มีวันยกเลิก
DaNeSh

2
นี่เป็นบางส่วนที่ถูกต้อง แต่ถ้าคุณมีห่วงโซ่สัญญายาววิธีนี้จะไม่ได้ผล
Veikko Karsikko

12

ฉันประหลาดใจมากที่ไม่มีใครพูดถึงPromise.raceว่าเป็นผู้สมัครสำหรับสิ่งนี้:

const actualPromise = new Promise((resolve, reject) => { setTimeout(resolve, 10000) });
let cancel;
const cancelPromise = new Promise((resolve, reject) => {
    cancel = reject.bind(null, { canceled: true })
})

const cancelablePromise = Object.assign(Promise.race([actualPromise, cancelPromise]), { cancel });

3
ฉันไม่เชื่อว่ามันจะได้ผล หากคุณเปลี่ยนคำสัญญาในการบันทึกการทำงานcancel()จะยังคงส่งผลให้มีการเรียกบันทึก `` const actualPromise = สัญญาใหม่ ((แก้ไขปฏิเสธ) => {setTimeout (() => {console.log ('actual called'); fix ()}, 10000)}); ``
shmck

2
คำถามคือวิธียกเลิกสัญญา (=> หยุดการถูกล่ามโซ่thenที่จะดำเนินการ) ไม่ใช่วิธีการยกเลิกsetTimeout(=> clearTimeout) หรือรหัสซิงโครนัสโดยที่ถ้าคุณไม่ใส่ if ไว้หลังแต่ละบรรทัด ( if (canceled) return) จะไม่สามารถทำได้ (อย่าทำแบบนี้)
Pho3nixHun

10
const makeCancelable = promise => {
    let rejectFn;

    const wrappedPromise = new Promise((resolve, reject) => {
        rejectFn = reject;

        Promise.resolve(promise)
            .then(resolve)
            .catch(reject);
    });

    wrappedPromise.cancel = () => {
        rejectFn({ canceled: true });
    };

    return wrappedPromise;
};

การใช้งาน:

const cancelablePromise = makeCancelable(myPromise);
// ...
cancelablePromise.cancel();

6

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

class CancelablePromise {
  constructor(executor) {
    let _reject = null;
    const cancelablePromise = new Promise((resolve, reject) => {
      _reject = reject;
      return executor(resolve, reject);
    });
    cancelablePromise.cancel = _reject;

    return cancelablePromise;
  }
}

การใช้งาน:

const p = new CancelablePromise((resolve, reject) => {
  setTimeout(() => {
    console.log('resolved!');
    resolve();
  }, 2000);
})

p.catch(console.log);

setTimeout(() => {
  p.cancel(new Error('Messed up!'));
}, 1000);

1
@dx_over_dt การแก้ไขของคุณน่าจะเป็นความคิดเห็นที่ดี แต่ไม่ใช่การแก้ไข โปรดทิ้งการแก้ไขที่สำคัญดังกล่าวไว้ในขอบเขตของ OP (เว้นแต่ว่าโพสต์นั้นจะถูกทำเครื่องหมายเป็น Community Wiki)
TylerH

@TylerH ดังนั้นจุดของการแก้ไขเพื่อแก้ไขการพิมพ์ผิดและสิ่งที่ชอบคืออะไร? หรือเพื่อปรับปรุงข้อมูลเมื่อมันล้าสมัย? ฉันยังใหม่กับความสามารถในการแก้ไขสิทธิ์การโพสต์ของคนอื่น
dx_over_dt

@dx_over_dt ใช่การแก้ไขคือการปรับปรุงโพสต์โดยการแก้ไขการพิมพ์ผิดข้อผิดพลาดทางไวยากรณ์และการเพิ่มการเน้นไวยากรณ์ (หากมีคนโพสต์โค้ดจำนวนมาก แต่ไม่ได้เยื้องหรือแท็กด้วย `` เป็นต้น) การเพิ่มเนื้อหาที่สำคัญเช่นคำอธิบายเพิ่มเติมหรือการให้เหตุผล / เหตุผลสำหรับสิ่งต่าง ๆ โดยทั่วไปจะเป็นขอบเขตของผู้โพสต์คำตอบ คุณมีอิสระที่จะแนะนำในความคิดเห็นและ OP จะได้รับแจ้งเกี่ยวกับความคิดเห็นจากนั้นสามารถตอบกลับหรือสามารถรวมคำแนะนำของคุณลงในโพสต์ได้เอง
TylerH

@dx_over_dt ข้อยกเว้นคือหากโพสต์ถูกทำเครื่องหมายว่า "Community Wiki" แสดงว่ามีวัตถุประสงค์เพื่อใช้เป็นโพสต์ที่ทำงานร่วมกัน (เช่น Wikipedia) หรือหากมีปัญหาร้ายแรงเกี่ยวกับโพสต์เช่นภาษาที่หยาบคาย / ไม่เหมาะสมเนื้อหาที่เป็นอันตราย / เป็นอันตราย ( เช่นคำแนะนำหรือรหัสที่อาจทำให้คุณติดไวรัสหรือทำให้คุณถูกจับกุมเป็นต้น) หรือข้อมูลส่วนบุคคลเช่นบันทึกสุขภาพหมายเลขโทรศัพท์บัตรเครดิต ฯลฯ อย่าลังเลที่จะลบสิ่งเหล่านั้นด้วยตัวคุณเอง
TylerH

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


4

AbortControllerสัญญาสามารถยกเลิกได้ด้วยความช่วยเหลือของ

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

ตัวอย่าง:

import "abortcontroller-polyfill";

let controller = new window.AbortController();
let signal = controller.signal;
let elem = document.querySelector("#status")

let example = (signal) => {
    return new Promise((resolve, reject) => {
        let timeout = setTimeout(() => {
            elem.textContent = "Promise resolved";
            resolve("resolved")
        }, 2000);

        signal.addEventListener('abort', () => {
            elem.textContent = "Promise rejected";
            clearInterval(timeout);
            reject("Promise aborted")
        });
    });
}

function cancelPromise() {
    controller.abort()
    console.log(controller);
}

example(signal)
    .then(data => {
        console.log(data);
    })
    .catch(error => {
        console.log("Catch: ", error)
    });

document.getElementById('abort-btn').addEventListener('click', cancelPromise);

Html


    <button type="button" id="abort-btn" onclick="abort()">Abort</button>
    <div id="status"> </div>

หมายเหตุ: จำเป็นต้องเพิ่ม polyfill ไม่รองรับในทุกเบราว์เซอร์

ตัวอย่างสด

แก้ไข elegant-lake-5jnh3


2

นี่คือการใช้งานของเราhttps://github.com/permettez-moi-de-construire/cancellable-promise

ใช้แล้วชอบ

const {
  cancellablePromise,
  CancelToken,
  CancelError
} = require('@permettezmoideconstruire/cancellable-promise')

const cancelToken = new CancelToken()

const initialPromise = SOMETHING_ASYNC()
const wrappedPromise = cancellablePromise(initialPromise, cancelToken)


// Somewhere, cancel the promise...
cancelToken.cancel()


//Then catch it
wrappedPromise
.then((res) => {
  //Actual, usual fulfill
})
.catch((err) => {
  if(err instanceOf CancelError) {
    //Handle cancel error
  }

  //Handle actual, usual error
})

ซึ่ง:

  • ไม่แตะ Promise API
  • ให้เราทำการยกเลิกเพิ่มเติมภายในการcatchโทร
  • อาศัยการยกเลิกที่ถูกปฏิเสธแทนที่จะได้รับการแก้ไขซึ่งแตกต่างจากข้อเสนอหรือการนำไปใช้งานอื่น ๆ

ยินดีต้อนรับดึงและแสดงความคิดเห็น


1

รุ่นง่าย :

เพียงแค่ให้ฟังก์ชันปฏิเสธ

function Sleep(ms,cancel_holder) {

 return new Promise(function(resolve,reject){
  var done=false; 
  var t=setTimeout(function(){if(done)return;done=true;resolve();}, ms);
  cancel_holder.cancel=function(){if(done)return;done=true;if(t)clearTimeout(t);reject();} 
 })
}

น้ำยาห่อหุ้ม (โรงงาน)

วิธีแก้ปัญหาที่ฉันพบคือการส่งผ่านอ็อบเจกต์ Cancel_holder มันจะมีฟังก์ชันยกเลิก หากมีฟังก์ชันยกเลิกแสดงว่ายกเลิกได้

ฟังก์ชันยกเลิกนี้ปฏิเสธคำสัญญาที่มีข้อผิดพลาด ('ยกเลิก')

ก่อนแก้ไขปฏิเสธหรือ on_cancel ป้องกันไม่ให้เรียกใช้ฟังก์ชันยกเลิกโดยไม่มีเหตุผล

ฉันพบว่าสะดวกในการดำเนินการยกเลิกโดยการฉีดยา

function cancelablePromise(cancel_holder,promise_fn,optional_external_cancel) {
  if(!cancel_holder)cancel_holder={};
  return new Promise( function(resolve,reject) {
    var canceled=false;
    var resolve2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; resolve.apply(this,arguments);}
    var reject2=function(){ if(canceled) return; canceled=true; delete cancel_holder.cancel; reject.apply(this,arguments);}
    var on_cancel={}
    cancel_holder.cancel=function(){
      if(canceled) return; canceled=true;

      delete cancel_holder.cancel;
      cancel_holder.canceled=true;

      if(on_cancel.cancel)on_cancel.cancel();
      if(optional_external_cancel)optional_external_cancel();

      reject(new Error('canceled'));
    };

    return promise_fn.call(this,resolve2,reject2,on_cancel);        
  });
}

function Sleep(ms,cancel_holder) {

 return cancelablePromise(cancel_holder,function(resolve,reject,oncacnel){

  var t=setTimeout(resolve, ms);
  oncacnel.cancel=function(){if(t)clearTimeout(t);}     

 })
}


let cancel_holder={};

// meanwhile in another place it can be canceled
setTimeout(function(){  if(cancel_holder.cancel)cancel_holder.cancel(); },500) 

Sleep(1000,cancel_holder).then(function() {
 console.log('sleept well');
}, function(e) {
 if(e.message!=='canceled') throw e;
 console.log('sleep interrupted')
})


1

หากรหัสของคุณถูกวางไว้ในคลาสคุณสามารถใช้มัณฑนากรได้ คุณมีมัณฑนากรดังกล่าวในutils-decorators ( npm install --save utils-decorators) จะยกเลิกการเรียกใช้เมธอดที่ได้รับการตกแต่งก่อนหน้านี้หากก่อนการแก้ไขการเรียกก่อนหน้านี้มีการเรียกใช้วิธีการเฉพาะนั้นอีกครั้ง

import {cancelPrevious} from 'utils-decorators';

class SomeService {

   @cancelPrevious()
   doSomeAsync(): Promise<any> {
    ....
   }
}

https://github.com/vlio20/utils-decorators#cancelprevious- วิธีการ


0

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

new Promise((resolve, reject) => {
    console.log('first chain link executed')
    resolve('daniel');
}).then(name => {
    console.log('second chain link executed')
    if (name === 'daniel') {
        // I don't want to continue the chain, return a new promise
        // that never calls its resolve function
        return new Promise((resolve, reject) => {
            console.log('unresolved promise executed')
        });
    }
}).then(() => console.log('last chain link executed'))

// VM492:2 first chain link executed
// VM492:5 second chain link executed
// VM492:8 unresolved promise executed

0

ตั้งค่าคุณสมบัติ "ยกเลิก" ในสัญญาว่าจะส่งสัญญาณthen()และcatch()ออกก่อนเวลา มีประสิทธิภาพมากโดยเฉพาะอย่างยิ่งใน Web Workers ที่มี microtasks อยู่ในคิวใน Promises จากonmessageตัวจัดการ

// Queue task to resolve Promise after the end of this script
const promise = new Promise(resolve => setTimeout(resolve))

promise.then(_ => {
  if (promise.canceled) {
    log('Promise cancelled.  Exiting early...');
    return;
  }

  log('No cancelation signaled.  Continue...');
})

promise.canceled = true;

function log(msg) {
  document.body.innerHTML = msg;
}


0

คำตอบของ @ Michael Yagudaev ใช้ได้กับฉัน

แต่คำตอบเดิมไม่ได้เชื่อมโยงคำสัญญาที่ห่อไว้กับ. catch () เพื่อจัดการการจัดการการปฏิเสธนี่คือการปรับปรุงของฉันที่อยู่เหนือคำตอบของ @Michael Yagudaev:

const makeCancelablePromise = promise => {
  let hasCanceled = false;
  const wrappedPromise = new Promise((resolve, reject) => {
    promise
      .then(val => (hasCanceled ? reject({ isCanceled: true }) : resolve(val)))
      .catch(
        error => (hasCanceled ? reject({ isCanceled: true }) : reject(error))
      );
  });

  return {
    promise: wrappedPromise,
    cancel() {
      hasCanceled = true;
    }
  };
};

// Example Usage:
const cancelablePromise = makeCancelable(
  new Promise((rs, rj) => {
    /*do something*/
  })
);
cancelablePromise.promise.then(() => console.log('resolved')).catch(err => {
  if (err.isCanceled) {
    console.log('Wrapped promise canceled');
    return;
  }
  console.log('Promise was not canceled but rejected due to errors: ', err);
});
cancelablePromise.cancel();

0

ถ้า p เป็นตัวแปรที่มี Promise p.then(empty);ควรยกเลิกคำสัญญาเมื่อในที่สุดเสร็จสมบูรณ์หรือถ้ามันเสร็จสมบูรณ์แล้ว (ใช่ฉันรู้ว่านี่ไม่ใช่คำถามดั้งเดิม แต่เป็นคำถามของฉัน) "ว่าง" function empty() {}คือ ฉันเป็นแค่มือใหม่และอาจจะคิดผิด แต่คำตอบอื่น ๆ เหล่านี้ดูเหมือนจะซับซ้อนเกินไป คำสัญญาควรจะเรียบง่าย


0

ฉันยังคงดำเนินการตามแนวคิดนี้อยู่ แต่นี่คือวิธีที่ฉันได้ใช้สัญญาที่ยกเลิกได้โดยใช้setTimeoutเป็นตัวอย่าง

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

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

  • อย่างที่สองคุณสามารถเพิ่มสิ่งที่คุณต้องการลงในวัตถุสัญญาที่สร้างขึ้นใหม่ได้อย่างมีความสุขก่อนที่คุณจะส่งคืนดังนั้นฉันจึงเพิ่งเพิ่มresolve()และreject()วิธีการทำให้มีอยู่ในตัว

  • ประการที่สามเคล็ดลับคือสามารถเข้าถึงตัวดำเนินการresolveและrejectฟังก์ชั่นในภายหลังได้ดังนั้นฉันจึงเก็บไว้ในวัตถุง่ายๆจากภายในการปิด

ฉันคิดว่าวิธีแก้ปัญหานั้นง่ายและฉันไม่เห็นปัญหาสำคัญใด ๆ

function wait(delay) {
  var promise;
  var timeOut;
  var executor={};
  promise=new Promise(function(resolve,reject) {
    console.log(`Started`);
    executor={resolve,reject};  //  Store the resolve and reject methods
    timeOut=setTimeout(function(){
      console.log(`Timed Out`);
      resolve();
    },delay);
  });
  //  Implement your own resolve methods,
  //  then access the stored methods
      promise.reject=function() {
        console.log(`Cancelled`);
        clearTimeout(timeOut);
        executor.reject();
      };
      promise.resolve=function() {
        console.log(`Finished`);
        clearTimeout(timeOut);
        executor.resolve();
      };
  return promise;
}

var promise;
document.querySelector('button#start').onclick=()=>{
  promise=wait(5000);
  promise
  .then(()=>console.log('I have finished'))
  .catch(()=>console.log('or not'));
};
document.querySelector('button#cancel').onclick=()=>{ promise.reject(); }
document.querySelector('button#finish').onclick=()=>{ promise.resolve(); }
<button id="start">Start</button>
<button id="cancel">Cancel</button>
<button id="finish">Finish</button>

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