Abbrechen einer Vanille-ECMAScript 6-Versprechenskette


110

Gibt es eine Methode zum Löschen des .thens einer JavaScript- PromiseInstanz?

Ich habe ein JavaScript-Testframework über QUnit geschrieben . Das Framework führt Tests synchron aus, indem jeder in a ausgeführt wird Promise. (Entschuldigen Sie die Länge dieses Codeblocks. Ich habe ihn so gut wie möglich kommentiert, damit er sich weniger langweilig anfühlt.)

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

Wenn bei einem Test eine Zeitüberschreitung auftritt, wird mein Zeitüberschreitungsversprechen assert.fail()für den Test aktiviert, sodass der Test als fehlgeschlagen markiert wird. Dies ist alles in Ordnung und gut, aber der Test wird weiterhin ausgeführt, da das Testversprechen ( result) noch auf die Lösung wartet.

Ich brauche einen guten Weg, um meinen Test abzubrechen. Ich kann dies tun, indem ich ein Feld auf dem Framework-Modul this.cancelTestoder etwas anderem erstelle und von Zeit zu Zeit (z. B. zu Beginn jeder then()Iteration) innerhalb des Tests überprüfe, ob ich abbrechen soll. Im Idealfall könnte ich jedoch $$(at).on("timeout", /* something here */)die verbleibenden then()s in meiner resultVariablen löschen , damit keiner der restlichen Tests ausgeführt wird.

Gibt es so etwas?

Schnelles Update

Ich habe es versucht Promise.race([result, at.promise]). Es hat nicht funktioniert.

Update 2 + Verwirrung

Um mich zu entsperren, habe ich mod.cancelTestinnerhalb der Testidee einige Zeilen mit dem / polling hinzugefügt . (Ich habe auch den Ereignisauslöser entfernt.)

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

Ich habe einen Haltepunkt in der catchAnweisung festgelegt, der getroffen wird. Was mich jetzt verwirrt, ist, dass die then()Anweisung nicht aufgerufen wird. Ideen?

Update 3

Ich habe das Letzte herausgefunden. fn.call()warf einen Fehler, den ich nicht abfing, so dass das Testversprechen abgelehnt wurde, bevor at.promise.catch()es behoben werden konnte.


Es ist möglich, mit ES6-Versprechungen zu stornieren, aber es ist keine Eigenschaft des Versprechens (vielmehr - es ist eine Eigenschaft der Funktion, die es zurückgibt). Ich kann ein kurzes Beispiel geben, wenn Sie interessiert sind.
Benjamin Gruenbaum

@BenjaminGruenbaum Ich weiß, es ist fast ein Jahr her, aber ich bin immer noch interessiert, ob Sie die Zeit haben, ein Beispiel zu schreiben. :)
dx_over_dt

1
Es ist ein Jahr her, aber es wurde zwei Tage vor gestern offiziell mit Stornierungsmarken und stornierbaren Versprechungen besprochen, die auf Stufe 1
Benjamin Gruenbaum

3
Die ES6-Antwort zum Abbrechen eines Versprechens ist beobachtbar. Sie können mehr darüber hier lesen: github.com/Reactive-Extensions/RxJS
Frank Goortani

Verknüpfung meiner Antwort zur Verwendung der PrexBibliothek für die Stornierung von Versprechungen.
Noseratio

Antworten:


75

Gibt es eine Methode zum Löschen des .thens einer JavaScript Promise-Instanz?

Nein, zumindest nicht in ECMAScript 6. Versprechen (und ihre thenHandler) können (leider) standardmäßig nicht storniert werden . Es gibt einige Diskussionen über es-Diskussion (z. B. hier ) darüber, wie dies richtig gemacht werden kann, aber welcher Ansatz auch immer gewinnen wird, wird nicht in ES6 landen.

Der aktuelle Standpunkt ist, dass Unterklassen es ermöglichen, stornierbare Versprechen mit Ihrer eigenen Implementierung zu erstellen (nicht sicher, wie gut das funktionieren wird) .

Bis das Sprachkomitee den besten Weg gefunden hat (hoffentlich ES7?), Können Sie weiterhin Userland Promise-Implementierungen verwenden, von denen viele nicht mehr funktionieren .

Die aktuelle Diskussion findet sich in den Entwürfen https://github.com/domenic/cancelable-promise und https://github.com/bergus/promise-cancellation .


2
"Ein bisschen Diskussion" - Ich kann auf vielleicht 30 Threads auf esdiscuss oder GitHub verlinken :) (ganz zu schweigen von Ihrer eigenen Hilfe bei der Stornierung in Bluebird 3.0)
Benjamin Gruenbaum

@BenjaminGruenbaum: Haben Sie diese Links bereit, um sie irgendwo zu teilen? Ich wollte schon lange Meinungen und Versuche zusammenfassen und einen Vorschlag zur Diskussion veröffentlichen, daher würde ich mich freuen, wenn ich zurückschauen kann, dass ich nichts vergessen habe.
Bergi

Ich habe sie bei der Arbeit zur Hand - also werde ich sie in 3-4 Tagen haben. Sie können die Versprechen-Stornierungsspezifikation unter Versprechen-aplus für einen guten Start überprüfen.
Benjamin Gruenbaum

1
@ LUH3417: "normale" Funktionen sind in dieser Hinsicht nur langweilig. Sie starten ein Programm und warten, bis es fertig ist - oder Sie killignorieren es in dem möglicherweise seltsamen Zustand, in dem die Nebenwirkungen Ihre Umgebung belassen haben (also werfen Sie das normalerweise auch weg, z. B. halbfertige Ausgaben). Nicht blockierende oder asynchrone Funktionen funktionieren jedoch in interaktiven Anwendungen, in denen Sie eine genauere Kontrolle über die Ausführung laufender Vorgänge wünschen.
Bergi

6
Domenic entfernte den TC39-Vorschlag ... ... cc @BenjaminGruenbaum
Sergio

50

Während es in ES6 keine Standardmethode gibt, gibt es eine Bibliothek namens Bluebird , um dies zu handhaben.

Es gibt auch einen empfohlenen Weg, der als Teil der Reaktionsdokumentation beschrieben wird. Es sieht ähnlich aus wie in Ihrem 2. und 3. Update.

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

Entnommen aus: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html


1
Diese Definition von annulliert lehnt nur das Versprechen ab. es hängt von der Definition von "storniert" ab.
Alexander Mills

1
Und was passiert, wenn Sie eine Reihe von Versprechungen stornieren möchten?
Matthieu Brucher

1
Das Problem bei diesem Ansatz ist, dass ein Versprechen, das niemals aufgelöst oder abgelehnt wird, niemals storniert wird.
DaNeSh

2
Dies ist teilweise richtig, aber wenn Sie eine lange Versprechenskette haben, würde dieser Ansatz nicht funktionieren.
Veikko Karsikko

11

Ich bin wirklich überrascht, dass niemand Promise.raceals Kandidat dafür erwähnt:

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
Ich glaube nicht, dass das funktioniert. Wenn Sie das Versprechen in "Protokoll" ändern, wird das Protokoll beim Ausführen cancel()weiterhin aufgerufen. `` `const actualPromise = neues Versprechen ((auflösen, ablehnen) => {setTimeout (() => {console.log ('tatsächlich aufgerufen'); auflösen ()}, 10000)}); `` `
shmck

2
Die Frage war, wie ein Versprechen storniert werden kann (=> verkettete thens beenden , um ausgeführt zu werden), nicht wie man setTimeout(=> clearTimeout) oder synchronen Code storniert , wobei if (canceled) returndies nur erreicht werden kann , wenn Sie nach jeder Zeile ( ) ein Wenn setzen . (Tun Sie dies nicht)
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;
};

Verwendung:

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

5

Es ist tatsächlich unmöglich, die Ausführung des Versprechens zu stoppen, aber Sie können die Ablehnung entführen und sie vom Versprechen selbst abrufen.

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

    return cancelablePromise;
  }
}

Verwendung:

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 Ihre Bearbeitung wäre ein großartiger Kommentar, aber keine Bearbeitung. Bitte überlassen Sie solche wesentlichen Änderungen dem Zuständigkeitsbereich des OP (es sei denn, der Beitrag ist natürlich als Community-Wiki gekennzeichnet).
TylerH

@ TylerH Also ist der Punkt der Bearbeitung, um Tippfehler und dergleichen zu beheben? Oder um Informationen zu aktualisieren, wenn sie veraltet sind? Ich bin neu in der Fähigkeit, die Beiträge anderer Leute zu bearbeiten.
dx_over_dt

@dx_over_dt Ja, das Bearbeiten dient dazu, Beiträge zu verbessern, indem Tippfehler und Grammatikfehler behoben und Syntaxhervorhebungen hinzugefügt werden (wenn jemand nur eine Menge Code veröffentlicht, ihn aber nicht einrückt oder zum Beispiel mit `` `markiert). Das Hinzufügen von inhaltlichen Inhalten wie zusätzlichen Erklärungen oder Begründungen / Begründungen für Dinge liegt normalerweise im Zuständigkeitsbereich der Person, die die Antwort veröffentlicht hat. Es steht Ihnen frei, dies in Kommentaren vorzuschlagen, und OP wird über den Kommentar informiert und kann darauf antworten oder Ihren Vorschlag einfach in den Beitrag selbst aufnehmen.
TylerH

@dx_over_dt Die Ausnahmen sind, wenn ein Beitrag als "Community-Wiki" gekennzeichnet ist und angibt, dass er als kollaborativer Beitrag dienen soll (z. B. Wikipedia), oder wenn es ernsthafte Probleme mit dem Beitrag gibt, wie z. B. unhöfliche / missbräuchliche Sprache, gefährliche / schädliche Inhalte ( zB Vorschläge oder Codes, die dazu führen können, dass Sie einen Virus bekommen oder verhaftet werden usw.), oder persönliche Informationen wie Gesundheitsakten, Telefonnummern, Kreditkarten usw.; Fühlen Sie sich frei, diese selbst zu entfernen.
TylerH

Es ist erwähnenswert, dass der Grund dafür, dass die Ausführung innerhalb eines Versprechens nicht gestoppt werden kann, darin besteht, dass JavaScript Single-Threaded ist. Während die Versprechen-Funktion ausgeführt wird, wird nichts anderes ausgeführt, sodass nichts das Anhalten der Ausführung auslösen kann.
dx_over_dt


2

Hier ist unsere Implementierung https://github.com/permettez-moi-de-construire/cancellable-promise

Gebraucht wie

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

welche :

  • Berührt Promise API nicht
  • Lassen Sie uns innerhalb des catchAnrufs weitere Stornierungen vornehmen
  • Verlassen Sie sich darauf, dass die Stornierung abgelehnt und nicht wie bei jedem anderen Vorschlag oder jeder anderen Umsetzung gelöst wird

Pulls und Kommentare sind willkommen


2

Versprechen kann mit Hilfe von storniert werden AbortController.

Gibt es dann eine Methode zum Löschen: Ja, Sie können das Versprechen mit AbortControllerObjekt ablehnen und dann promisewerden alle Blöcke umgangen und gehen direkt zum Fangblock.

Beispiel:

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>

Hinweis: Polyfill muss hinzugefügt werden, was nicht in allen Browsern unterstützt wird.

Live-Beispiel

Bearbeiten Sie elegant-lake-5jnh3


1

einfache Version :

Geben Sie einfach die Ablehnungsfunktion aus.

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

eine Wraper-Lösung (Fabrik)

Die Lösung, die ich gefunden habe, besteht darin, ein cancel_holder-Objekt zu übergeben. es wird eine Abbruchfunktion haben. Wenn es eine Abbruchfunktion hat, kann es abgebrochen werden.

Diese Abbruchfunktion lehnt das Versprechen mit Fehler ab ('abgebrochen').

Vor dem Auflösen, Ablehnen oder on_cancel verhindern Sie, dass die Abbruchfunktion ohne Grund aufgerufen wird.

Ich habe es als zweckmäßig empfunden, die Abbruchaktion durch Injektion zu bestehen

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

Versuchen Sie, Versprechen abzubrechen : 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();
});

1

Wenn Ihr Code in einer Klasse platziert ist, können Sie dafür einen Dekorateur verwenden. Sie haben einen solchen Dekorateur in den Utils-Dekoratoren ( npm install --save utils-decorators). Der vorherige Aufruf der dekorierten Methode wird abgebrochen, wenn vor dem Auflösen des vorherigen Aufrufs ein weiterer Aufruf für diese bestimmte Methode erfolgt ist.

import {cancelPrevious} from 'utils-decorators';

class SomeService {

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

https://github.com/vlio20/utils-decorators#cancelprevious-method


0

Wenn Sie verhindern möchten, dass alle Thens / Catches ausgeführt werden, können Sie dies tun, indem Sie ein Versprechen einfügen, das sich niemals auflösen lässt. Es hat wahrscheinlich Speicherleck-Reprocusionen, aber es wird das Problem beheben und sollte in den meisten Anwendungen nicht zu viel Speicherverschwendung verursachen.

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

Stellen Sie im Versprechen eine "abgebrochene" Eigenschaft ein, um zu signalisieren then()und catch()vorzeitig zu beenden. Dies ist sehr effektiv, insbesondere bei Web-Workern, bei denen vorhandene Mikrotasks in Promises von Handlern in die Warteschlange gestellt wurden 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 Yagudaevs Antwort funktioniert für mich.

Aber die ursprüngliche Antwort hat das verpackte Versprechen nicht mit .catch () verkettet, um die Bearbeitung von Ablehnungen zu handhaben. Hier ist meine Verbesserung zusätzlich zu der Antwort von @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

Wenn p eine Variable ist, die ein Versprechen enthält, p.then(empty);sollte das Versprechen verworfen werden, wenn es schließlich abgeschlossen ist oder wenn es bereits abgeschlossen ist (ja, ich weiß, dass dies nicht die ursprüngliche Frage ist, aber es ist meine Frage). "leer" ist function empty() {}. Ich bin nur ein Anfänger und wahrscheinlich falsch, aber diese anderen Antworten scheinen zu kompliziert. Versprechen sollen einfach sein.


0

Ich arbeite noch an dieser Idee, aber hier ist, wie ich ein stornierbares Versprechen mit implementiert habe setTimeout Beispiel .

Die Idee ist, dass ein Versprechen gelöst oder abgelehnt wird, wann immer Sie es entschieden haben. Es sollte also eine Frage der Entscheidung sein, wann Sie stornieren möchten, das Kriterium erfüllen und dann die reject()Funktion selbst aufrufen .

  • Erstens denke ich, dass es zwei Gründe gibt, ein Versprechen vorzeitig zu beenden: es zu Ende zu bringen (was ich als Entschlossenheit bezeichnet habe ) und abzubrechen (was ich als Ablehnung bezeichnet habe ). Das ist natürlich nur mein Gefühl. Natürlich gibt es eine Promise.resolve()Methode, aber sie befindet sich im Konstruktor selbst und gibt ein Dummy-gelöstes Versprechen zurück. Diese Instanzmethode resolve()löst tatsächlich ein instanziiertes Versprechungsobjekt auf.

  • Zweitens können Sie gerne alles , was Sie wie in ein neu erstelltes Versprechen Objekt hinzufügen , bevor Sie es zurück, und so habe ich gerade hinzugefügt haben resolve()und reject()Methoden zu machen , in sich abgeschlossen.

  • Drittens besteht der Trick darin, später auf den Executor resolveund die rejectFunktionen zugreifen zu können , sodass ich sie einfach in einem einfachen Objekt innerhalb des Verschlusses gespeichert habe.

Ich denke, die Lösung ist einfach und ich kann keine größeren Probleme damit sehen.

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>

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.