React: `isMounted` einstellen

Erstellt am 14. Nov. 2015  ·  48Kommentare  ·  Quelle: facebook/react

isMounted ist für ES6-Klassen bereits nicht verfügbar, und wir haben bereits eine Warnung, die besagt, dass wir sie "könnten", sie zu entfernen, aber wir haben eigentlich kein Github-Problem, um sie zu verwerfen. Gemäß unseren heutigen Diskussionen sind wir uns im Grunde einig, dass wir uns von isMounted entfernen und es verwerfen werden. Wir müssen noch einige gute Geschichten rund um Versprechen (und verwandte Anwendungsfälle) herausfinden.

Dieses Thema dient dazu, den Fortschritt in Richtung dieses Ziels zu verfolgen.

Zum Hintergrund lesen Sie bitte:

Hilfreichster Kommentar

Diese einfache Methode kann verwendet werden, um jedem Versprechen eine Stornierung hinzuzufügen

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

EDIT: Auf Korrektheit/Vollständigkeit aktualisiert.

WIE BENUTZT MAN

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Alle 48 Kommentare

Ich bin damit nicht einverstanden. Insbesondere ES6-Versprechen können nicht zuverlässig auf componentWillUnmount abgebrochen werden, daher öffnet das Entfernen der einzigen Möglichkeit, um zu überprüfen, ob die Komponente vor setState oder einer anderen Aktion gemountet ist, den Weg für viele schwer zu verfolgende asynchrone Fehler.

@yaycmyk Also die Zeile:

Wir müssen noch einige gute Geschichten rund um Versprechen (und verwandte Anwendungsfälle) herausfinden.

Bitte lesen Sie insbesondere die von mir aufgeführten Hintergrundprobleme: https://github.com/facebook/react/issues/2787#issuecomment -68738793

Ich habe die Kommentare gelesen. Ich finde die Probleme einfach unlösbar.

Warum können Versprechen nicht zuverlässig storniert werden? Irgendwelche Quellen/Beweise/Beispiele?

Am Montag, 16. November 2015, schrieb Evan Jacobs [email protected] :

Ich bin damit nicht einverstanden. Insbesondere ES6-Versprechen können nicht zuverlässig sein
abgebrochen bei componentWillUnmount, also die einzige Möglichkeit zu entfernen, um zu überprüfen, ob
die Komponente wird vor setState gemountet oder eine andere Aktion öffnet das
Weg für viele schwer zu verfolgende asynchrone Fehler.

@nvartolomei Schauen Sie sich die ES6-Versprechensspezifikation an.

Dies ist ein längerfristiges Ziel, nicht etwas, das sofort geschieht. Aber wir möchten die Planungen und Diskussionen an einem einzigen Ort und nicht über Kommentare in jeder Ausgabe hinweg verfolgen, wenn dies auftaucht. Wir sind uns des Problems bewusst, dass Versprechen derzeit nicht stornierbar sind, was ein Hauptgrund dafür ist, dass wir dies noch nicht getan haben.

@yaycmyk Um ein sehr komplexes Problem zu isMounted zur Vermeidung von setState für nicht gemountete Komponenten löst nicht wirklich das Problem, dass die setState Warnung versuchte darauf hinzuweisen - tatsächlich verbirgt sie das Problem nur. Außerdem ist das Aufrufen von setState als Ergebnis eines Versprechens sowieso ein bisschen ein Anti-Muster, da es Race-Conditions verursachen kann, die beim Testen nicht unbedingt auftauchen. Daher wollen wir es loswerden und eine "Best Practice"-Empfehlung für die Verwendung von Versprechen mit React herausarbeiten.

Ich stimme zu, dass die Probleme ein wenig undurchschaubar sind, aber das liegt hauptsächlich daran, dass es sich um ein komplexes Problem handelt, das wir noch herausfinden und für das wir noch keine vorgefertigte Antwort haben.

Das Aufrufen von setState als Ergebnis eines Versprechens ist sowieso ein bisschen ein Anti-Muster, da es Race-Conditions verursachen kann, die beim Testen nicht unbedingt auftauchen

Wir können uns darauf einigen, dass wir nicht einverstanden sind. Es gibt Zeiten, in denen Inhalte asynchron abgerufen werden und Sie kein vollständiges erneutes Rendern durchlaufen möchten, um diese Inhalte nach der Auflösung einzufügen. Ich verwende es speziell in einer unendlichen Tabellenansichtsimplementierung, bei der ein vollständiges virtuelles Rendern unnötig wäre.

Sie können eine Zusage möglicherweise nicht stornieren, aber Sie können festlegen, dass die Komponente beim Unmounten wie folgt dereferenziert wird:

const SomeComponent = React.createClass({
    componentDidMount() {
        this.protect = protectFromUnmount();

        ajax(/* */).then(
            this.protect( // <-- barrier between the promise and the component
                response => {this.setState({thing: response.thing});}
            )
        );
    },
    componentWillUnmount() {
        this.protect.unmount();
    },
});

Der wichtige Unterschied besteht darin, dass, wenn this.protect.unmount() in componentWillUnmount aufgerufen wird, alle Callbacks dereferenziert werden, was bedeutet, dass die Komponente dereferenziert wird, und dann, wenn das Promise abgeschlossen ist, nur ein No-Op aufgerufen wird. Dies sollte Speicherlecks im Zusammenhang mit Promises-Referenzen nicht gemounteten Komponenten verhindern. Quelle für protectFromUnmount

Diese einfache Methode kann verwendet werden, um jedem Versprechen eine Stornierung hinzuzufügen

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

EDIT: Auf Korrektheit/Vollständigkeit aktualisiert.

WIE BENUTZT MAN

const somePromise = new Promise(r => setTimeout(r, 1000));

const cancelable = makeCancelable(somePromise);

cancelable
  .promise
  .then(() => console.log('resolved'))
  .catch(({isCanceled, ...error}) => console.log('isCanceled', isCanceled));

// Cancel promise
cancelable.cancel();

Das Auflisten von Möglichkeiten, ES6 zu unterstützen, verspricht, sie stornierbar zu machen, ist nebensächlich. Die Absicht sollte darin bestehen, eine Lösung bereitzustellen, die MIT der Spezifikation funktioniert, anstatt zu versuchen, UM die Spezifikation zu arbeiten.

Ich stimme zu. Anstatt einfach zu prüfen, ob die Komponente noch montiert ist, wenn wir das Versprechensergebnis erhalten, müssen wir auf alle Arten von Magie zurückgreifen, um unser Versprechen von der Komponente, in die es sein Ergebnis setzen soll, "abzubinden" und klar gegen die Art und Weise Versprechen ankämpfen sind entworfen.
Für mich fühlt es sich an, als würde man eine Lösung überbauen, bei der ein einfacher Test der einfachste Weg ist, dies zu erledigen.

Wir können eine einfache Überprüfung durchführen, indem wir einfach:

React.createClass(function() {
  componentDidMount: function() {
    this._isMounted = true;

    ajax(/* */).then(this.handleResponse);
  }

  handleResponse: function(response) {
    if (!this._isMounted) return; // Protection

    /* */
  }

  componentWillUnmount: function() {
    this._isMounted = false;
  }
});

Dies ist natürlich meine Meinung, aber es scheint mir, dass das asynchrone Laden von Daten mit einem Promise innerhalb einer React-Komponente ein so häufiges Szenario ist, dass es von React abgedeckt werden sollte, anstatt unseren eigenen Boilerplate-Code schreiben zu müssen.

Das Problem ist, dass wir, um den wahren Mount-Zustand zu erreichen, einen Listener hinzufügen müssen, wenn React den DOM-Mount-Prozess in jeder Komponente beendet (derselbe, der componentDidMount anhängt, falls definiert), aber es wirkt sich auf die Perf aus, weil wir dies nicht brauchen es überall zu brachen. Komponente hört standardmäßig nicht auf DOM-Mount bereit, da ComponentDidMount nicht definiert ist.

Was wäre, wenn setState ein verkettetes Versprechen übergeben werden könnte, das sich in die gewünschten Zustandsänderungen auflöst? Wenn die Komponente ausgehängt wird, wird deren Ergebnis ignoriert, wenn ausstehende Zusagen vorhanden sind.

@istarkov schönes Muster, gefällt

// create a new promise
const [response, cancel] = await cancelable(fetch('/api/data'));

// cancel it
cancel();

Da ich neu bei React bin und Dokumentationen lese, nur um das hier rauszuschmeißen: Der Load Initial Data via Ajax- Tipp verwendet .isMounted() , also stimmt die Website nicht mit der Website überein. Es wäre großartig, einen vollständigen Tipp zum Abbrechen des anfänglichen Ladens in componentWillUnmount , möglicherweise mithilfe des obigen Musters von

@dtertman Behoben in https://github.com/facebook/react/pull/5870 , wird online sein, wenn die Dokumente ausgewählt werden.

@jimfb danke, ich bin mir nicht sicher, wie ich das bei der Suche übersehen habe.

@istarkov nicht sicher, ob dies beabsichtigt war, aber Ihr makeCancelable nicht, wenn das ursprüngliche Versprechen fehlschlägt. Wenn das ursprüngliche Promise abgelehnt wird, wird kein Handler aufgerufen.

Dies scheint nicht ideal zu sein, da Sie möglicherweise dennoch einen Fehler beim ursprünglichen Versprechen behandeln möchten.

Hier ist mein Vorschlag für ein makeCancelable , das eine Ablehnung im ursprünglichen Versprechen behandelt:

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

Ich bin mir nicht sicher, wo ich stehe, wenn es eine gute Idee ist, kündbare Versprechen zu machen, aber wenn wir kündbare Versprechen machen wollen, sollten wir das zugrunde liegende Verhalten beibehalten :).

@vpontis : +1:

@istarkov Ihr ursprünglicher Beitrag wird hier referenziert: https://facebook.github.io/react/blog/2015/12/16/ismount-antipattern.html

Möchten Sie Ihren Beitrag aktualisieren oder soll ich dem Autor des Beitrags eine Nachricht senden?

@vpontis Danke, ich werde es reparieren! (https://github.com/facebook/react/pull/6152)

Hey @jimfb , viel Spaß beim

Ein weiterer Bugfix in der makeCancelable Funktion: es kann ein UnhandledPromiseRejectionWarning in neueren Node-Versionen verursachen (insbesondere wenn Tests mit einer neuen Node-Version ausgeführt werden). Eine der Änderungen in Knoten 6.6.0 besteht darin, dass alle nicht behandelten Promise-Ablehnungen zu einer Warnung führen. Der vorhandene Code von @vpontis hatte separate then und catch Aufrufe auf demselben Basisversprechen. Dies schafft effektiv _zwei_ Versprechen, eines, das nur den Erfolg behandelt, und eines, das nur Fehler behandelt. Das bedeutet, dass im Fehlerfall das erste Promise vom Knoten als nicht behandelte Promise-Ablehnung betrachtet wird.

Die Lösung ist ziemlich einfach: Verketten Sie einfach die beiden Aufrufe, sodass ein Erfolgs- und ein Fehlerhandler ein Versprechen abgeben. Hier ist der feste Code:

const makeCancelable = (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;
    },
  };
};

@alangpierce Das ist sehr nahe an richtig, aber nicht ganz; Wenn resolve() oder reject() aus irgendeinem Grund synchron auf ein aufgelöstes Promise wirft, werden beide Handler aufgerufen.

Die Lösung besteht darin, das Muster .then(onFulfilled, onRejected) zu verwenden:

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

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

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

Ist diese makeCanceable-Lösung nicht effektiv dieselbe wie der isMounted()-Aufruf, wenn man sich Punkt 3 ansieht, warum isMounted() veraltet ist:

Aufruf von setState, wenn eine Komponente vollständig ausgehängt ist.
Dies ist ein starker Hinweis darauf, dass ein asynchroner Rückruf nicht ordnungsgemäß bereinigt wird. Leider machen es Mainstream-JS-APIs sehr einfach, das Bereinigen hängender asynchroner Callbacks zu vermeiden.

Ein Rückruf ist keine große Sache. Dieser Rückruf hängt jedoch an Objekten und Zwischenrückrufen, Versprechen und Abonnements. Wenn dies bei vielen Ihrer Komponenten der Fall ist, werden Sie schnell auf Speicherprobleme stoßen

makeCancellable erstellt lediglich ein weiteres Promise, das am Ende einen Verweis auf Funktionen enthält, die einen Verweis auf die Komponente enthalten. Die makeCancellable-Lösung verschiebt nur die boolesche Eigenschaft isMounted in das Promise.

Um das GC-Problem zu lösen, müssen Sie beim Aufrufen von Cancel() etwas auf Null setzen. andernfalls haben Sie immer noch eine Referenzkette vom asynchronen Prozess zur Komponente.

class CancellableDeferred {
  constructor(request) {
    this.deferred = $.Deferred();

    request.then((data) => {
      if (this.deferred != null) {
        this.deferred.resolve(data);
      }
    });

    request.fail((data) => {
      if (this.deferred != null) {
        this.deferred.reject(data);
      }
    });
  }

  cancel() {
    this.deferred = null;
  } 

  promise() {
    return this.deferred.promise();
  }
}

-> so würde ich es mit deferred-Objekten von jQuery machen. Ich kenne die Promise-API nicht wirklich, daher weiß ich nicht, wie sie aussehen würde. Außerdem wird das Zurückgestellte nicht abgelehnt, wenn Cancel() aufgerufen wurde und das Zurückgestellte nicht aufgelöst wurde. Wahrscheinlich haben die Leute eine andere Meinung darüber, wie dies funktionieren sollte.

Kette sieht also ungefähr so ​​aus:

AJAX Request -> Closure -> CancellableDeferredInstance -> JQuery Deferred -> Component

dann sieht es nach abbrechen so aus:

AJAX Request -> Closure -> CancellableDeferredInstance /object reference now null/ JQuery Deferred -> Component

Die AJAX-Anfrage verhindert also nicht mehr, dass die Komponente GCd wird [vorausgesetzt, ich habe die Implementierung nicht irgendwo vermasselt, indem ich versehentlich einen Verweis auf deferred gehalten habe. yay Schließungen....]

Hallo @benmmurphy , ich

makeCancellable ermöglicht die Garbage-Collection einer React-Komponente, wenn sie ausgehängt wird. Ich erkläre es.

makeCancellable erstellt lediglich ein weiteres Promise, das am Ende einen Verweis auf Funktionen enthält, die einen Verweis auf die Komponente enthalten. Die makeCancellable-Lösung verschiebt nur die boolesche Eigenschaft isMounted in das Promise.

Ohne makeCancellable :

handleError() {
  if (this.isMounted()) {
    console.log('ERROR')
  }
}

Mit makeCancellable :

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');
})

Ohne makeCancellable Sie immer noch eine Referenz auf this so dass die Komponente nach dem Aushängen nicht in die Garbage Collection aufgenommen werden kann. Aber im anderen Fall wird der Fail-Handler des kündbaren Promises aufgerufen, sobald die Komponente ausgehängt wird, sodass keine Referenzen mehr herumhängen.

@vpontis

Ich habe etwas Nodejs-Code, der das Problem veranschaulicht. Die Foo-Komponente wird erst dann mit GC verknüpft, wenn der asynchrone Callback resolve auf null gesetzt wurde. Nehmen wir zum Beispiel an, Sie starten eine Ajax-Anfrage, deren Auflösung 30 Sekunden dauert, dann wird die Komponente ausgehängt. Dann wird die Komponente 30 Sekunden lang nicht GCd sein. Dies ist eines der Probleme, die sie mit der Einstellung von isMount() zu lösen versuchen.

npm install promise
npm install weak

node --expose-gc gc.js
first gc Foo {}
after first gc Foo {}
after resolve = null Foo {}
foo gc'd
after second gc {}

https://gist.github.com/benmmurphy/aaf35a44a6e8a1fbae1764ebed9917b6

BEARBEITEN:

Entschuldigung, dass ich an dir vorbei geredet habe, aber als ich den Beitrag zum ersten Mal gelesen habe, habe ich nicht verstanden, was du damit sagen wolltest, aber jetzt denke ich, dass ich es tue. Ich denke, was Sie damit sagen möchten, ist, dass die Komponente nicht als vom Versprechen referenziert angesehen wird, da der Fehlerrückruf keinen Verweis auf die Komponente enthält (oder keinem Verweis auf die Komponente folgt). Dies ist tatsächlich wahr. Nun, der erste Teil stimmt. Bei dieser Argumentation gibt es jedoch Probleme:

1) Auch wenn der Fehlerhandler in Ihrem Beispiel keinen Verweis auf die Komponente hat, wird der then() Callback normalerweise verwenden. Zum Beispiel wird das then Handle normalerweise this.setState(...) tun.
2) obwohl der Fehlerhandler in Ihrem Beispiel keinen Verweis auf die Komponente hat, die die meisten Fehlerhandler verwenden. zum Beispiel werden sie etwas tun wie:

promise.then(...).fail((reason) => {
  if (reason.isCancelled) return;
  console.log('ERROR');

  this.setState({error: true});
})

3) Obwohl wir wissen, dass der Code dem then() Callback nicht folgt und wir wissen, dass er die Funktion verlässt, nachdem die isCancelled Variable überprüft wurde, weiß der GC dies nicht.

und bevor jemand mein Beispiel oder etwas, das darauf basiert, verwendet, stellen Sie sicher, dass Sie testen, ob es tatsächlich richtig GCs funktioniert. Ich habe meine noch nicht getestet und es würde mich nicht überraschen, wenn es nicht funktionieren würde, weil ich einen dummen Fehler gemacht habe :/

in Bezug auf die Versprechen-API funktioniert dies für mich in nodejs in Bezug auf GC. Allerdings würde ich es vorziehen, die Parameter _resolve , _reject nicht in der Nähe der Schließungen zu haben, da ich nicht sicher bin, ob dies gemäß der JS-Spezifikation garantiert funktioniert oder ob es zufällig funktioniert weil der Knoten einige Optimierungen durchführt. Kann eine Implementierung alle sichtbaren Variablen erfassen oder nur die Variablen, auf die in der Closure verwiesen wird? Ich weiß nicht, vielleicht kann sich jemand, der JS wirklich versteht, einschalten und erklären :)

var makeCancelable = (promise) => {
  let resolve;
  let reject;

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

    promise.then((val) => {
       if (resolve != null) resolve(val)
    });
    promise.catch((error) => {
       if (reject != null) reject(error)
    });
  });

  return {
    promise: wrappedPromise,
    cancel() {
      resolve = null;
      reject = null;
    },
  };
};

Wird die isMounted-Funktion in 16.0 entfernt?

Vorschlag für eine kleine Verbesserung mit @istarkov- Code:

const makeCancelable = (promise) => {
    let hasCanceled_ = false
    promise.then((val) =>
        hasCanceled_ ? reject({isCanceled: true}) : resolve(val)
    )
    .catch((error) =>
        hasCanceled_ ? reject({isCanceled: true}) : reject(error)
    )

    return {
        promise,
        cancel() {
            hasCanceled_ = true
        }
    }
}

Das neue Versprechen ist nur überflüssig.

Das neue Versprechen ist nur überflüssig.

@BnayaZil Sie rufen die Funktionen resolve und reject , aber es ist unklar, woher diese stammen. Meinten Sie Promise.resolve und Promise.reject ? In diesem Fall würden Sie immer noch ein neues Versprechen zurückgeben.

Vor einigen Tagen wurde der DOM-Spezifikation eine neue API hinzugefügt, mit der Sie fetch()-Anfragen abbrechen können. Diese API ist noch in keinem Browser implementiert, aber ich habe ein Polyfill dafür erstellt, das auf NPM verfügbar war "abortcontroller-polyfill". Das Polyfill macht im Wesentlichen dasselbe wie der von @istarkov gepostete

Einzelheiten hier:
https://mo.github.io/2017/07/24/abort-fetch-abortcontroller-polyfill.html

Da React.createClass in React 16 nicht mehr existiert und das neue create-react-class Paket eine klare veraltete Meldung für isMounted , werde ich dies schließen.

Ich stimme @benmmurphy zu, dass die Lösung von Grunde die gleiche ist wie die Verwendung von isMounted() da sie das Garbage-Collection-Problem nicht löst.

Die Lösung von @benmmurphy ist näher, löscht jedoch die falschen Variablen, sodass die Promise-Handler nicht dereferenziert werden.

Der Schlüssel besteht darin, eine Funktion nach oben durch die Closure zu übergeben, die die Handler dereferenziert:

const makeCancelable = promise => {
  let cancel = () => {};

  const wrappedPromise = new Promise((resolve, reject) => {
    cancel = () => {
      resolve = null;
      reject = null;
    };

    promise.then(
      val => {
        if (resolve) resolve(val);
      },
      error => {
        if (reject) reject(error);
      }
    );
  });

  wrappedPromise.cancel = cancel;
  return wrappedPromise;
};

Weitere Erläuterungen, warum diese Lösung die Garbage Collection ermöglicht und nicht die vorherigen Lösungen, finden Sie hier .

Ich ging voran und verwandelte dies in ein npm-Paket, Papierkorb . Und da der Anwendungsfall reagieren ist, habe ich eine HOC-Komponente erstellt, die Versprechen verfolgt und sie storniert, wenn die Komponente nicht mehr bereitgestellt wird,

Bearbeiten: mein Fehler , ich habe mir gerade thrashable angesehen , und es bricht auch Versprechen ab. Dennoch ist das Muster unten IMO eine kleine Verbesserung.

Keine dieser Lösungen storniert Versprechen, die ohne Verlängerung storniert werden können, indem ein für immer ausstehendes Versprechen aufgenommen wird.

function makeCancelable(promise) {
  let active = true;
  return {
    cancel() {active = false},
    promise: promise.then(
      value => active ? value : new Promise(()=>{}),
      reason => active ? reason : new Promise(()=>{})
    )
  }
}

// used as above:

const {promise, cancel} = makeCancelable(Promise.resolve("Hey!"))

promise.then((v) => console.log(v)) // never logs
cancel()

hier leben

In Bezug auf GC und Kaffee müssen möglicherweise Feinheiten ausgebügelt werden, aber dieses Muster stellt sicher, dass das zurückgegebene Versprechen wirklich storniert wird und es nicht durchgesickert werden kann (ich habe es in der Vergangenheit implementiert).

@pygy Danke für die Antwort!

Leider lässt Ihre Lösung die Garbage Collection immer noch nicht zu. Sie haben im Wesentlichen gerade die Lösung von @istarkov umgeschrieben, die eine Bedingung verwendet.

Sie können dies leicht testen, indem Sie diese Implementierung in den Papierkorb verschieben und die Tests

Ihre Implementierung kann auch Fehler nicht richtig behandeln.

Es ist 2018, gibt es einen noch besseren Ansatz als den oben genannten?

Ja, Sie können einige Navigationsframeworks verwenden, die eine Dokumentation haben, die doppelt so groß ist wie die von Reactive Native, aber sehr professionell ist

Diese Schnipsel zum "Stornieren" eines Versprechens sind IMHO nicht so toll. Die stornierten Versprechen werden immer noch nicht aufgelöst, bis das ursprüngliche Versprechen aufgelöst wird. Die Speicherbereinigung wird also erst durchgeführt, wenn Sie nur einen isMounted-Trick verwendet haben.

Ein richtiger stornierbarer Versprechen-Wrapper müsste ein zweites Versprechen und Promise.race verwenden. dh Promise.race([originalPromise, cancelationPromise])

Die Lösung von @benmmurphy ist näher, löscht jedoch die falschen Variablen, sodass die Promise-Handler nicht dereferenziert werden.

Ich denke, meine Lösung funktioniert, aber ich weiß nicht genug darüber, was die Javascript-Laufzeit verspricht, um sicher zu sein. Wenn Sie die Lösung unter dem Knoten in Ihrem Testkabelbaum ausführen, wird der Wert korrekt ausgewertet. Meine Lösung hat die Funktionen zum Auflösen/Ablehnen einem höheren Bereich zugewiesen und diese Werte dann auf Null gesetzt, wenn Cancel aufgerufen wurde. Allerdings waren die Funktionen im unteren Umfang noch vorhanden, aber nicht referenziert. Ich denke, moderne Javascript-Engines erfassen keine Variablen in einer Closure, es sei denn, sie werden referenziert. Ich denke, dies war früher ein großes Problem, bei dem Leute versehentlich DOM-Lecks erzeugten, weil sie Dinge taten wie: var element = findDOM(); element.addEventListener('click', function() {}); und -Element würde in der Closure referenziert, obwohl es in der Closure nicht verwendet wurde.

@hjylewis @benmmurphy warum müssen wir Handler dereferenzieren?? Nachdem Handler ausgeführt wurden, findet die Garbage Collection auf jeden Fall statt, oder ??

Diese Schnipsel zum "Stornieren" eines Versprechens sind IMHO nicht so toll. Die stornierten Versprechen werden immer noch nicht aufgelöst, bis das ursprüngliche Versprechen aufgelöst wird. Die Speicherbereinigung wird also erst durchgeführt, wenn Sie nur einen isMounted-Trick verwendet haben.

Ein richtiger stornierbarer Versprechen-Wrapper müsste ein zweites Versprechen und Promise.race verwenden. dh Promise.race([originalPromise, cancelationPromise])

@hjylewis und meine arbeiten Sie tatsächlich, können Sie es mit schwachem Knoten überprüfen. Aber wenn ich sie noch einmal anschaue, stimme ich zu, dass keiner von ihnen eigenwillig geschriebener Versprechenscode ist. als Versprechensbenutzer würden Sie wahrscheinlich erwarten, dass ein 'storniertes' Versprechen im abgelehnten Zustand aufgelöst wird, und keiner von ihnen tut dies. Im Fall einer Komponente ist dies möglicherweise jedoch eine einfacher zu verwendende Lösung, da Sie keinen zusätzlichen Code schreiben müssen, um den Ablehnungshandler zu ignorieren.

Ich denke, ein idiosynkratisches ablehnbares Versprechen würde Promise.race([]) verwenden, um ein stornierbares Versprechen zu erstellen. es funktioniert, weil, wenn ein Promise aufgelöst wird, die ausstehenden Callbacks gelöscht werden, so dass es zu diesem Zeitpunkt keine Referenzkette vom Browsernetzwerk zu Ihrer Komponente geben würde, da keine Referenz mehr zwischen dem Race Promise und der Komponente vorhanden wäre.

Ich bin gespannt, ob es irgendwie möglich ist, Promise.all() mit diesen stornierbaren Versprechen zu verwenden und unaufgefangene Fehler in der Browser-Konsole zu vermeiden ... weil ich nur den ersten Abbruchfehler abfangen kann, bleiben andere unabgefangen.

Es ist 2018, gibt es einen noch besseren Ansatz als den oben genannten?

Ein besserer Ansatz, um eine Versprechensausführung abzubrechen, z. B. setTimeout, API-Aufrufe usw. Es ist 2019 😭 😞

Es gibt einen Thread zur Kündigung von Versprechen auf TC39 (glaube ich), er ist hier von Bedeutung (vielleicht .. nicht sicher)
https://github.com/tc39/proposal-cancellation/issues/24

Ein besserer Ansatz, um eine Versprechensausführung abzubrechen, z. B. setTimeout, API-Aufrufe usw. Es ist 2019 😭 😞

Suchen wir sowas wie

const promise = new Promise(r => setTimeout(r, 1000))
  .then(() => console.log('resolved'))
  .catch(()=> console.log('error'))
  .canceled(() => console.log('canceled'));

// Cancel promise
promise.cancel();
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen