javascript - एक वेनिला ECMAScript 6 वादा श्रृंखला रद्द करें




promise cancellation (9)

वहाँ एक जावास्क्रिप्ट वादा उदाहरण के .then s समाशोधन के लिए एक विधि है?

कम से कम ECMAScript 6 में नहीं। वादे (और उनके then हैंडलर) डिफ़ॉल्ट रूप से अकारण होते हैं (दुर्भाग्य से) । इसे सही तरीके से कैसे किया जाए, इस बारे में es-चर्चा (उदाहरण के लिए) पर थोड़ी चर्चा है, लेकिन जो भी दृष्टिकोण जीतेगा वह ES6 में नहीं उतरेगा।

वर्तमान दृष्टिकोण यह है कि उपवर्ग अपने स्वयं के कार्यान्वयन का उपयोग करके रद्द करने योग्य वादों को बनाने की अनुमति देगा (यह सुनिश्चित नहीं है कि यह कितना अच्छा काम करेगा)

जब तक भाषा कमेटी ने सबसे अच्छा तरीका निकाला है (ईएस 7 उम्मीद है?) तब भी आप उपयोगकर्तालैंड प्रॉमिस इम्प्लीमेंटेशन का उपयोग कर सकते हैं, जिनमें से कई फीचर रद्द हो जाते हैं।

वर्तमान चर्चा https://github.com/domenic/cancelable-promise और https://github.com/bergus/promise-cancellation ड्राफ्ट में है।

वहाँ एक जावास्क्रिप्ट Promise उदाहरण के .then s समाशोधन के लिए एक विधि है?

मैंने QUnit शीर्ष पर एक जावास्क्रिप्ट परीक्षण रूपरेखा QUnit । फ्रेमवर्क हर एक को एक Promise में चलाकर सिंक्रोनाइज़ 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() देगा assert.fail() परीक्षण में विफल के रूप में चिह्नित किया गया है, जो सभी अच्छी तरह से और अच्छा है, लेकिन परीक्षण अभी भी जारी है क्योंकि परीक्षण का वादा ( result ) अभी भी इंतजार कर रहा है इसे हल करने के लिए।

मुझे अपना टेस्ट रद्द करने के लिए एक अच्छे तरीके की जरूरत है। मैं इसे रूपरेखा मॉड्यूल पर एक क्षेत्र बनाकर कर सकता हूं। यह this.cancelTest या कुछ और, और हर बार जांच कर रहा है (उदाहरण के लिए प्रत्येक के शुरुआत में then() पुनरावृत्ति) परीक्षण के भीतर कि क्या रद्द करना है। हालाँकि, आदर्श रूप से, मैं अपने result चर पर शेष then() को साफ करने के लिए $$(at).on("timeout", /* something here */) का उपयोग कर सकता हूं, ताकि बाकी परीक्षा में से कोई भी न चले। ।

क्या ऐसा कुछ मौजूद है?

शीघ्र नवीनीकरण

मैंने Promise.race([result, at.promise]) का उपयोग करने की कोशिश की। यह काम नहीं किया।

अद्यतन 2 + भ्रम

मुझे अनवरोधित करने के लिए, मैंने परीक्षण विचार के भीतर mod.cancelTest / मतदान के साथ कुछ पंक्तियाँ 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() कथन को नहीं बुलाया जा रहा है। विचार?

अपडेट ३

आखिरी बात का पता लगाया। fn.call() एक त्रुटि फेंक रहा था जिसे मैंने नहीं पकड़ा था, इसलिए परीक्षण का वादा at.promise.catch() से पहले अस्वीकार कर सकता था।


जबकि ईएस 6 में ऐसा करने का कोई मानक तरीका नहीं है, लेकिन इसे संभालने के लिए 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


मैं वास्तव में आश्चर्यचकित हूं कि कोई भी इसके लिए एक उम्मीदवार के रूप में 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 });

यदि p एक चर है जिसमें एक वादा है, तो p.then(empty); वादा पूरा होने पर इसे खारिज कर देना चाहिए या अगर यह पहले से ही पूरा हो गया है (हाँ, मुझे पता है कि यह मूल प्रश्न नहीं है, लेकिन यह मेरा सवाल है)। "खाली" function empty() {} । मैं अभी शुरुआत कर रहा हूं और शायद गलत हूं, लेकिन ये अन्य जवाब बहुत जटिल हैं। वादे सरल होने चाहिए।


यहां हमारा कार्यान्वयन 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
})

कौन कौन से :

  • प्रॉमिस एपीआई को स्पर्श न करें
  • आइए हम catch कॉल के अंदर और रद्द करें
  • किसी अन्य प्रस्ताव या कार्यान्वयन के विपरीत हल किए जाने के बजाय रद्द किए जाने पर भरोसा करना

खींचने और टिप्पणियों का स्वागत करते हैं



संकेत करने के लिए then() और catch() जल्दी बाहर निकलने के लिए "रद्द" संपत्ति सेट करें। यह बहुत प्रभावी है, विशेष रूप से वेब वर्कर्स में जो मौजूदा माइक्रोटेक को onmessage हैंडलर से 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;
}


वादा-निरर्थक प्रयास करें: https://www.npmjs.com/package/promise-abortable

$ npm install promise-abortable
import AbortablePromise from "promise-abortable";

const timeout = new AbortablePromise((resolve, reject, signal) => {
  setTimeout(reject, timeToLive, error);
  signal.onabort = resolve;
});

Promise.resolve(fn()).then(() => {
  timeout.abort();
});

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







es6-promise