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?
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"]();
einfach:
var promiseResolve, promiseReject;
var promise = new Promise(function(resolve, reject){
promiseResolve = resolve;
promiseReject = reject;
});
promiseResolve();
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
})
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
@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();
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
});
});
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.
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!
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');
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();
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.
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();
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)
}
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();
});
...
});
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
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.");
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