我一直在使用ES6 Promise。
通常,像这样构建和使用Promise
new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
但是为了灵活性,我一直在做类似下面的事情来解决问题。
var outsideResolve;
var outsideReject;
new Promise(function(resolve, reject) {
outsideResolve = resolve;
outsideReject = reject;
});
以后
onClick = function(){
outsideResolve();
}
这很好用,但有更简单的方法吗?如果没有,这是一个好习惯吗?
答案 0 :(得分:99)
简单:
{{1}}
答案 1 :(得分:75)
这里的派对迟到了,但另一种方法是使用Deferred对象。你基本上拥有相同数量的样板,但是如果你想传递它们并且可能在它们的定义之外解决它,它会很方便。
天真实施:
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版本:
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
})
答案 2 :(得分:70)
不,没有其他方法可以做到这一点 - 我唯一可以说的是这个用例并不常见。就像费利克斯在评论中所说的那样 - 你所做的将会持续发挥作用。
值得一提的是,promise构造函数以这种方式运行的原因是抛出安全性 - 如果您的代码在promise构造函数内运行时没有预料到会发生异常,它将变成拒绝,这种形式抛出安全 - 将抛出的错误转换为拒绝是很重要的,有助于维护可预测的代码。
对于这个抛出安全的原因,承诺构造函数被选择为延迟(这是一种替代的承诺构造方式,允许你正在做的事情) - 至于最佳实践 - 我传递元素和改为使用promise构造函数:
var p = new Promise(function(resolve, reject){
this.onclick = resolve;
}.bind(this));
出于这个原因 - 只要你可以使用promise构造函数而不是导出函数 - 我建议你使用它。每当你可以避免两者 - 避免两者和链。
注意,你永远不应该对if(condition)
之类的东西使用promise构造函数,第一个例子可以写成:
var p = Promise[(someCondition)?"resolve":"reject"]();
答案 3 :(得分:16)
我在2015年为我的框架提出了一个解决方案。我称这种承诺为任务
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
答案 4 :(得分:13)
我喜欢@JonJaques的回答,但我想更进一步。
如果您绑定then
和catch
然后绑定Deferred
对象,那么它会完全实现Promise
API,您可以将其视为承诺和await
它等等。
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();
答案 5 :(得分:9)
辅助方法可以减轻这些额外开销,并为您提供相同的jQuery感觉。
function Deferred() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve, reject };
}
用法是
const { promise, resolve, reject } = Deferred();
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
return promise;
类似于jQuery
const dfd = $.Deferred();
displayConfirmationDialog({
confirm: dfd.resolve,
cancel: dfd.reject
});
return dfd.promise();
虽然在一个用例中这个简单的原生语法很好
return new Promise((resolve, reject) => {
displayConfirmationDialog({
confirm: resolve,
cancel: reject
});
});
答案 6 :(得分:7)
我正在使用一个辅助函数来创建所谓的“统一承诺”-
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;
}
查看完整的工作示例-
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);
})();
编辑: 我创建了一个名为flat-promise的NPM程序包,并且该代码也可以在on GitHub中使用。
答案 7 :(得分:3)
是的,可以。通过为浏览器环境使用CustomEvent
API。并在node.js环境中使用事件发射器项目。由于问题中的代码段是针对浏览器环境的,因此这里是一个可行的示例。
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>
我希望这个答案有用!
答案 8 :(得分:2)
您可以将Promise包装在一个类中。
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.
答案 9 :(得分:2)
我们的解决方案是使用闭包来存储解析/拒绝函数,并附加一个函数来扩展promise本身。
以下是模式:
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;
}
使用它:
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');
答案 10 :(得分:2)
感谢在此主题中发布的每个人。我创建了一个模块,该模块包括前面所述的Defer()对象以及在其上构建的其他一些对象。它们都利用Promises和简洁的Promise回调语法在程序内实现通信/事件处理。
队列:基于Promise链接的执行队列。
rp = require("repeatable-promise")
答案 11 :(得分:1)
此处的许多答案与this article中的最后一个示例相似。
我正在缓存多个Promises,并且resolve()
和reject()
函数可以分配给任何变量或属性。结果,我可以使这段代码更加紧凑:
function defer(obj) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
}
以下是使用此版本的defer()
来将FontFace
负载承诺与另一个异步过程结合在一起的简化示例:
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
更新:如果要封装对象,有2种选择:
function defer(obj = {}) {
obj.promise = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
return obj;
}
let deferred = defer();
和
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
let deferred = new Deferred();
答案 12 :(得分:1)
接受的答案是错误的。使用范围和引用非常容易,尽管它可能会使Promise 纯粹主义者生气:
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);
创建诺言时,我们实质上是在获取对resolve函数的引用,然后我们将其返回,以便可以在外部进行设置。
控制台将在一秒钟内输出:
> foo
答案 13 :(得分:0)
为此我写了一个小库。 https://www.npmjs.com/package/@inf3rno/promise.exposed
我使用了其他人编写的工厂方法,但是我也覆盖了then
,catch
,finally
方法,因此您也可以解决那些最初的承诺。
在没有外部执行者的情况下解决承诺:
const promise = Promise.exposed().then(console.log);
promise.resolve("This should show up in the console.");
通过外部执行者的setTimeout进行竞速:
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);
如果您不想污染全局名称空间,则有一个无冲突模式:
const createExposedPromise = require("@inf3rno/promise.exposed/noConflict");
const promise = createExposedPromise().then(console.log);
promise.resolve("This should show up in the console.");
答案 14 :(得分:0)
如何创建劫持拒绝并返回的功能?
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();
答案 15 :(得分:0)
我汇总了完成该工作的要点:https://gist.github.com/thiagoh/c24310b562d50a14f3e7602a82b4ef13
这是您应该如何使用它:
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();
});
...
});
答案 16 :(得分:0)
首先在浏览器或节点上启用--allow-natives-syntax
const p = new Promise(function(resolve, reject){
if (someCondition){
resolve();
} else {
reject();
}
});
onClick = function () {
%ResolvePromise(p, value)
}
答案 17 :(得分:0)
我制作了一个名为manual-promise
的库,用来代替Promise
。此处的其他答案都不能用作Promise
的替代品,因为它们使用代理或包装。
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
答案 18 :(得分:0)
我发现自己在某些情况下也缺少Deferred模式。您始终可以在ES6 Promise的顶部创建一个:
export default class Deferred<T> {
private _resolve: (value: T) => void = () => {};
private _reject: (value: T) => void = () => {};
private _promise: Promise<T> = new Promise<T>((resolve, reject) => {
this._reject = reject;
this._resolve = resolve;
})
public get promise(): Promise<T> {
return this._promise;
}
public resolve(value: T) {
this._resolve(value);
}
public reject(value: T) {
this._reject(value);
}
}
答案 19 :(得分:0)
另一种解决方案,可以从外部解决Promise
class Lock {
#lock; // Promise to be resolved (on release)
release; // Release lock
id; // Id of lock
constructor(id) {
this.id = id
this.#lock = new Promise((resolve) => {
this.release = () => {
if (resolve) {
resolve()
} else {
Promise.resolve()
}
}
})
}
get() { return this.#lock }
}
let lock = new Lock(... some id ...);
...
lock.get().then(()=>{console.log('resolved/released')})
lock.release() // Excpected 'resolved/released'
答案 20 :(得分:0)
由于我没有找到想要的东西,因此我将分享我在完成此问题后想要实现的目标。
场景:我有3个不同的API,且响应可能相同,因此我想在一个函数中处理Promise的完成和错误处理。这就是我所做的:
switch (method) {
case 'get': {
this.handleHttpPromise(apiService.get(url));
break;
}
case 'post': {
if (jsonData) {
this.handleHttpPromise(apiService.post(url, jsonData));
}
break;
}
// (...)
}
{{1}}
答案 21 :(得分:0)
我想分享一些不同的东西,扩展这个话题。
有时您希望在解析时在同一地址(属性或变量)自动重新创建“任务承诺”。可以创建一个可以做到这一点的外部解析器。
具有外部解析器的重复承诺示例。每当调用解析器时,都会在相同的地址/变量/属性处创建一个新的承诺。
let resolvePromise;
let thePromise;
const setPromise = (resolve) => {
resolvePromise = () => {
resolve();
thePromise = new Promise(setPromise);
}
}
thePromise = new Promise(setPromise);
(async () => {
let i = 0;
while (true) {
let msg = (i % 2 === 0) ? 'Tick' : 'Tock';
document.body.innerHTML = msg;
setTimeout(resolvePromise, 1000);
await thePromise;
i++;
}
})();