webentwicklung-frage-antwort-db.com.de

Lösen Sie Javascript Promise außerhalb des Funktionsumfangs auf

Ich habe ES6 Promise verwendet.

Normalerweise wird ein Versprechen so konstruiert und verwendet

new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

Aber ich habe etwas unternommen, um die Entschlossenheit aus Gründen der Flexibilität nach außen zu bringen.

var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) { 
    outsideResolve = resolve; 
    outsideReject = reject; 
});

Und später

onClick = function(){
    outsideResolve();
}

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

190
Morio

Nein, es gibt keine andere Möglichkeit, dies zu tun. Ich kann nur sagen, dass dieser Anwendungsfall nicht sehr häufig ist. Wie Felix in dem Kommentar sagte - was Sie tun, wird immer funktionieren.

Es ist erwähnenswert, dass der Grund, warum sich der Versprechenkonstruktor auf diese Weise verhält, die Wurfsicherheit ist. Wenn eine Ausnahme auftritt, die Sie nicht erwartet haben, während der Code innerhalb des Versprechenkonstruktors ausgeführt wird, wird dies zu einer Ablehnung. Diese Form der Wurfsicherheit konvertiert geworfene Fehler in Ablehnungen sind wichtig und helfen, vorhersagbaren Code zu erhalten.

Aus diesem Grund wurde der Versprechenkonstruktor aus Versetzungsgründen gewählt (dies ist eine alternative Konstruktionsmethode, die das zulässt, was Sie tun). Als Best Practices würde ich das Element übergeben und stattdessen den Versprechenkonstruktor verwenden:

var p = new Promise(function(resolve, reject){
    this.onclick = resolve;
}.bind(this));

Aus diesem Grund empfehle ich Ihnen, immer wenn Sie can den Promise-Konstruktor über den Export der Funktionen verwenden. Wann immer Sie beides vermeiden können - vermeiden Sie beides und Ketten.

Beachten Sie, dass Sie den Versprechenkonstruktor niemals für Dinge wie if(condition) verwenden sollten. Das erste Beispiel könnte folgendermaßen geschrieben werden:

var p = Promise[(someCondition)?"resolve":"reject"]();
62

einfach:

var promiseResolve, promiseReject;

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

promiseResolve();
88
carter

Etwas spät für die Party hier, aber eine andere Möglichkeit wäre die Verwendung eines Deferred -Objekts. Sie haben im Wesentlichen die gleiche Menge an Boilerplate, aber es ist praktisch, wenn Sie sie weitergeben 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
})
72
Jon Jaques

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

function createPromise(handler){
  var _resolve, _reject;

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

    handler(resolve, reject);
  })

  promise.resolve = _resolve;
  promise.reject = _reject;

  return promise;
}

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

promise.resolve(200) // resolve from outside
14
Maxmaxmaximus

@JonJaques hat mir gefallen, aber ich wollte noch einen Schritt weiter gehen.

Wenn Sie then und catch binden, dann implementiert das Deferred-Objekt die Promise-API vollständig und Sie können es als Versprechen und await und so behandeln.

class DeferredPromise {
  constructor() {
    this._promise = new Promise((resolve, reject) => {
      // assign the resolve and reject functions to `this`
      // making them usable on the class instance
      this.resolve = resolve;
      this.reject = reject;
    });
    // bind `then` and `catch` to implement the same interface as Promise
    this.then = this._promise.then.bind(this._promise);
    this.catch = this._promise.catch.bind(this._promise);
    this[Symbol.toStringTag] = 'Promise';
  }
}

const deferred = new DeferredPromise();
console.log('waiting 2 seconds...');
setTimeout(() => {
  deferred.resolve('whoa!');
}, 2000);

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

someAsyncFunction();

11
Rico Kahler

Eine Hilfsmethode würde diesen zusätzlichen Aufwand verringern und Ihnen das gleiche jQuery-Gefühl vermitteln.

function Deferred() {
    let resolve;
    let reject;
    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });
    return { promise, resolve, reject };
}

Verwendung wäre

const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
    confirm: resolve,
    cancel: reject
});
return promise;

Welches ist jQuery ähnlich

const dfd = $.Deferred();
displayConfirmationDialog({
    confirm: dfd.resolve,
    cancel: dfd.reject
});
return dfd.promise();

In einem Anwendungsfall ist diese einfache, native Syntax jedoch in Ordnung

return new Promise((resolve, reject) => {
    displayConfirmationDialog({
        confirm: resolve,
        cancel: reject
    });
});
8
Cory Danielson

Ich verwende eine Hilfsfunktion, um ein "pauschales Versprechen" zu erstellen -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
      resolve = res;
      reject = rej;
    });

    return { promise, resolve, reject };
}

Und ich benutze es gerne so -

function doSomethingAsync() {

    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;

}

Siehe ausführliches Arbeitsbeispiel -

function flatPromise() {

    let resolve, reject;

    const promise = new Promise((res, rej) => {
        resolve = res;
        reject = rej;
    });

    return { promise, resolve, reject };
}

function doSomethingAsync() {
    
    // Get your promise and callbacks
    const { resolve, reject, promise } = flatPromise();

    // Do something amazing...
    setTimeout(() => {
        resolve('done!');
    }, 500);

    // Pass your promise to the world
    return promise;
}

(async function run() {

    const result = await doSomethingAsync()
        .catch(err => console.error('rejected with', err));
    console.log(result);

})();

Edit: Ich habe ein NPM-Paket mit dem Namen flat-promise erstellt und der Code ist auch auf GitHub verfügbar.

5
Arik

Ja, du kannst. Mithilfe der CustomEvent-API für die Browserumgebung. Und mit einem Event-Emitter-Projekt in node.js-Umgebungen. Da sich das fragliche Snippet auf die Browserumgebung bezieht, finden Sie hier ein funktionierendes Beispiel dafür.

function myPromiseReturningFunction(){
  return new Promise(resolve => {
    window.addEventListener("myCustomEvent", (event) => {
       resolve(event.detail);
    }) 
  })
}


myPromiseReturningFunction().then(result => {
   alert(result)
})

document.getElementById("p").addEventListener("click", () => {
   window.dispatchEvent(new CustomEvent("myCustomEvent", {detail : "It works!"}))
})
<p id="p"> Click me </p>

Ich hoffe diese Antwort ist nützlich!

3

Unsere Lösung bestand darin, Verschlüsse zum Speichern der Auflösungs-/Ablehnungsfunktionen zu verwenden und zusätzlich eine Funktion zur Erweiterung des Versprechens selbst hinzuzufügen. 

Hier ist das Muster:

function getPromise() {

    var _resolve, _reject;

    var promise = new Promise((resolve, reject) => {
        _reject = reject;
        _resolve = resolve;
    });

    promise.resolve_ex = (value) => {
       _resolve(value);
    };

    promise.reject_ex = (value) => {
       _reject(value);
    };

    return promise;
}

Und es benutzen:

var promise = getPromise();

promise.then(value => {
    console.info('The promise has been fulfilled: ' + value);
});

promise.resolve_ex('hello');  
// or the reject version 
//promise.reject_ex('goodbye');
2
Steven Spungin

Viele der Antworten hier ähneln dem letzten Beispiel in dieser Artikel . Ich speichere mehrere Versprechen, und die Funktionen resolve() und reject() können jeder Variablen oder Eigenschaft zugewiesen werden. Infolgedessen kann ich diesen Code etwas kompakter gestalten:

function defer(obj) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
}

Hier ist ein vereinfachtes Beispiel für die Verwendung dieser Version von defer(), um ein FontFace -Lade-Promise mit einem anderen asynchronen Prozess zu kombinieren:

function onDOMContentLoaded(evt) {
    let all = []; // array of Promises
    glob = {};    // global object used elsewhere
    defer(glob);
    all.Push(glob.promise);
    // launch async process with callback = resolveGlob()

    const myFont = new FontFace("myFont", "url(myFont.woff2)");
    document.fonts.add(myFont);
    myFont.load();
    all.Push[myFont];
    Promise.all(all).then(() => { runIt(); }, (v) => { alert(v); });
}
//...
function resolveGlob() {
    glob.resolve();
}
function runIt() {} // runs after all promises resolved 

Update: 2 Alternativen für den Fall, dass Sie das Objekt einkapseln möchten:

function defer(obj = {}) {
    obj.promise = new Promise((resolve, reject) => {
        obj.resolve = resolve;
        obj.reject  = reject;
    });
    return obj;
}
let deferred = defer();

und

class Deferred {
    constructor() {
        this.promise = new Promise((resolve, reject) => {
            this.resolve = resolve;
            this.reject  = reject;
        });
    }
}
let deferred = new Deferred();
1
jamess

Sie können das Versprechen in eine Klasse einpacken. Das Beispiel verwendet die Flow-Typisierung, kann aber natürlich auch ohne verwendet werden:

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

        this.promise.resolve = this.resolve;
        this.promise.reject = this.reject;

        return this.promise;
    }
    promise;
    resolve;
    reject;
}

// How to use.
const promise = new Deferred((resolve, reject) => {
  // Use like normal Promise.
});

promise.resolve(); // Resolve from any context.
0
Hinrich

Wie wäre es, eine Funktion zu erstellen, um die Zurückweisung zu hijacken und zurückzugeben?

function createRejectablePromise(handler) {
  let _reject;

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

    handler(resolve, reject);
  })

  promise.reject = _reject;
  return promise;
}

// Usage
const { reject } = createRejectablePromise((resolve) => {
  setTimeout(() => {
    console.log('resolved')
    resolve();
  }, 2000)

});

reject();
0
nikksan

aktivieren Sie zuerst --allow-natives-syntax im Browser oder auf dem Knoten

const p = new Promise(function(resolve, reject){
    if (someCondition){
        resolve();
    } else {
        reject();
    } 
});

onClick = function () {
    %ResolvePromise(p, value)
}

0
Equitable

Ich habe ein Gist zusammengestellt, das diese Aufgabe erfüllt: https://Gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13

so solltest du es verwenden:

import ExternalizedPromiseCreator from '../externalized-promise';

describe('ExternalizedPromise', () => {
  let fn: jest.Mock;
  let deferredFn: jest.Mock;
  let neverCalledFn: jest.Mock;
  beforeEach(() => {
    fn = jest.fn();
    deferredFn = jest.fn();
    neverCalledFn = jest.fn();
  });

  it('resolve should resolve the promise', done => {
    const externalizedPromise = ExternalizedPromiseCreator.create(() => fn());

    externalizedPromise
      .promise
      .then(() => deferredFn())
      .catch(() => neverCalledFn())
      .then(() => {
        expect(deferredFn).toHaveBeenCalled();
        expect(neverCalledFn).not.toHaveBeenCalled();
        done();
      });

    expect(fn).toHaveBeenCalled();
    expect(neverCalledFn).not.toHaveBeenCalled();
    expect(deferredFn).not.toHaveBeenCalled();

    externalizedPromise.resolve();
  });
  ...
});
0
thiagoh

Ich habe eine Bibliothek namens manual-promise erstellt, die als Ersatz für Promise fungiert. Keine der anderen Antworten hier ersetzt Promise, da sie Proxys oder Wrapper verwenden.

yarn add manual-promise

npn install manual-promise


import { ManualPromise } from "manual-promise";

const prom = new ManualPromise();

prom.resolve(2);

// actions can still be run inside the promise
const prom2 = new ManualPromise((resolve, reject) => {
    // ... code
});


new ManualPromise() instanceof Promise === true

https://github.com/zpxp/manual-promise#readme

0
jeohd

Ich habe dafür eine kleine Bibliothek geschrieben. https://www.npmjs.com/package/@inf3rno/promise.exposed

Ich habe die Methode der Factory-Methode verwendet, die andere geschrieben haben, aber ich habe auch die Methoden then, catch, finally überschrieben, so dass Sie auch das ursprüngliche Versprechen auflösen können.

Versprechen ohne Executor von außen lösen:

const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");

Rennen mit dem setTimeout des Executors von außen:

const promise = Promise.exposed(function (resolve, reject){
    setTimeout(function (){
        resolve("I almost fell asleep.")
    }, 100000);
}).then(console.log);

setTimeout(function (){
    promise.resolve("I don't want to wait that much.");
}, 100);

Es gibt einen konfliktfreien Modus, wenn Sie den globalen Namespace nicht verschmutzen möchten:

const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
0
inf3rno

Akzeptierte Antwort ist falsch. Es ist ziemlich einfach, Umfang und Verweise zu verwenden, obwohl es Promise Puristen verärgern kann:

const createPromise = () => {
    let resolver;
    return [
        new Promise((resolve, reject) => {
            resolver = resolve;
        }),
        resolver,
    ];
};

const [ promise, resolver ] = createPromise();
promise.then(value => console.log(value));
setTimeout(() => resolver('foo'), 1000);

Wir greifen im Wesentlichen auf den Verweis auf die Auflösungsfunktion zurück, wenn das Versprechen erstellt wird, und geben ihn zurück, damit er extern festgelegt werden kann.

In einer Sekunde gibt die Konsole Folgendes aus:

> foo
0
Ali