525 Stimmen

Wie kann ich auf Promise-Auflösungsrückrufe außerhalb des Scope des Promise-Constructor-Rückrufs zugreifen?

Normalerweise wird ein Promise konstruiert und wie folgt verwendet:

new Promise((resolve, reject) => {
  const obj = new MyEventEmitter();
  obj.onsuccess = (event) => { resolve(event.result); };
  obj.onerror = (event) => { reject(event.error); };
});

Aber in letzter Zeit habe ich etwas Ähnliches wie das folgende gemacht, um den Resolver außerhalb des Executor-Callbacks flexibler zu handhaben:

let outsideResolve;
let outsideReject;
new Promise((resolve, reject) => {
  outsideResolve = resolve; 
  outsideReject = reject; 
});

Und später:

onClick = function() {
  outsideResolve();
}

Dies funktioniert gut, aber gibt es einen einfacheren Weg, dies zu tun? Wenn nicht, ist dies eine gute Praxis?

265voto

carter Punkte 4793

Einfach:

var promiseResolve, promiseReject;

var promise = new Promise(function(resolve, reject){
  promiseResolve = resolve;
  promiseReject = reject;
});

promiseResolve();

168voto

Jon Jaques Punkte 4006

Ziemlich spät hier, aber eine andere Möglichkeit, dies zu tun, wäre die Verwendung eines Deferred-Objekts. Im Grunde genommen haben Sie die gleiche Menge an Boilerplate, aber es ist praktisch, wenn Sie sie herumreichen und möglicherweise außerhalb ihrer Definition auflösen möchten.

Naive Implementierung:

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject)=> {
      this.reject = reject
      this.resolve = resolve
    })
  }
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(()=> {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(result => {
  console.log(result) // 42
})

ES5 Version:

function Deferred() {
  var self = this;
  this.promise = new Promise(function(resolve, reject) {
    self.reject = reject
    self.resolve = resolve
  })
}

function asyncAction() {
  var dfd = new Deferred()

  setTimeout(function() {
    dfd.resolve(42)
  }, 500)

  return dfd.promise
}

asyncAction().then(function(result) {
  console.log(result) // 42
})

127voto

Benjamin Gruenbaum Punkte 259076

Ab ECMAScript 2024 – ja, verwendet Promise.withResolvers().

Für frühere Versionen gibt es keine andere Möglichkeit, dies zu tun – aber eines kann ich sagen: Dieser Anwendungsfall ist nicht sehr verbreitet. Wie Felix im Kommentar sagte – was du tust, wird konsistent funktionieren.

Es ist erwähnenswert, dass das Verhalten des Promise-Konstruktors aus Gründen der Fehlerbehandlung so ist: Wenn eine nicht antizipierte Ausnahme auftritt, während Ihr Code im Promise-Konstruktor ausgeführt wird, wird diese zu einem Abbruch umgewandelt. Diese Form der Fehlerbehandlung – das Umwandeln von geworfenen Fehlern in Abbrüche – ist wichtig und hilft dabei, vorhersehbaren Code zu erstellen.

Aus diesem Grund wurde der Executor-Rückruf anstelle von verzoegerten Rückrufen (die eine alternative Möglichkeit zur Erstellung von Versprechungen sind und das zulassen, was Sie tun) gewählt – was bewährte Verfahren betrifft: Ich würde das Element übergeben und den Promise-Konstruktor verwenden:

const p = new Promise((resolve, reject) => {
    this.onclick = resolve;
});

Aus diesem Grund – wann immer Sie können den Promise-Konstruktor anstelle der Funktionen exportieren – empfehle ich Ihnen, ihn zu verwenden. Wann immer Sie beide vermeiden können – vermeiden Sie beide und verketten.

38voto

Rico Kahler Punkte 15352

Ich mochte die Antwort @JonJaques, aber ich wollte einen Schritt weiter gehen.

Wenn Sie `then` und `catch` an das `Deferred`-Objekt binden, implementiert es vollständig die `Promise`-API und Sie können es als Promise behandeln und darauf `await` und ähnliches.

Editor's Note: Ich empfehle dieses Muster nicht mehr, da zum Zeitpunkt des Schreibens `Promise.prototype.finally` noch nicht existierte, dann wurde es eingeführt... Dies könnte auch bei anderen Methoden passieren, daher empfehle ich, die Promise-Instanz mit den Funktionen `resolve` und `reject` zu erweitern:

function createDeferredPromise() {
  let resolve
  let reject

  const promise = new Promise((thisResolve, thisReject) => {
    resolve = thisResolve
    reject = thisReject
  })

  return Object.assign(promise, {resolve, reject})
}

Upvote die Antwort von jemand anderem.

class DeferredPromise {
  constructor() {
    this._promise = new Promise((resolve, reject) => {
      // Die `resolve`- und `reject`-Funktionen dem `this` zuweisen
      // und sie auf der Klasseninstanz nutzbar machen
      this.resolve = resolve;
      this.reject = reject;
    });
    // `then` und `catch` binden, um die gleiche Schnittstelle wie Promise zu implementieren
    this.then = this._promise.then.bind(this._promise);
    this.catch = this._promise.catch.bind(this._promise);
    this.finally = this._promise.finally.bind(this._promise);
    this[Symbol.toStringTag] = 'Promise';
  }
}

const deferred = new DeferredPromise();
console.log('2 Sekunden warten...');
setTimeout(() => {
  deferred.resolve('WOW!');
}, 2000);

async function someAsyncFunction() {
  const value = await deferred;
  console.log(value);
}

someAsyncFunction();

31voto

Maxmaxmaximus Punkte 1945

Eine Lösung, die ich 2015 für mein Framework gefunden habe. Ich habe diese Art von Versprechen Task genannt

function createPromise(handler){
  var resolve, reject;

  var promise = new Promise(function(_resolve, _reject){
    resolve = _resolve; 
    reject = _reject;
    if(handler) handler(resolve, reject);
  })

  promise.resolve = resolve;
  promise.reject = reject;
  return promise;
}

// erstellen
var promise = createPromise()
promise.then(function(data){ alert(data) })

// auflösen von außen
promise.resolve(200)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X