我一直在使用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();
}

这很好,但是否有更简单的方法来做到这一点?如果不是,这是一个好的实践吗?


当前回答

公认的答案是错误的。使用范围和引用非常简单,尽管这可能会让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);

我们实际上是在创建promise时获取resolve函数的引用,并返回该引用,以便可以在外部设置它。

在一秒钟内控制台将输出:

> foo

其他回答

类版本,在Typescript中:

export class Deferred<T> {
    public readonly promise: Promise<T>
    private resolveFn!: (value: T | PromiseLike<T>) => void
    private rejectFn!: (reason?: any) => void

    public constructor() {
        this.promise = new Promise<T>((resolve, reject) => {
            this.resolveFn = resolve
            this.rejectFn = reject
        })
    }

    public reject(reason?: any): void {
        this.rejectFn(reason)
    }

    public resolve(param: T): void {
        this.resolveFn(param)
    }
}

不,没有其他方法可以做到这一点——我唯一能说的是这个用例不是很常见。就像Felix在评论中说的那样——你所做的将始终有效。

值得一提的是,promise构造函数以这种方式运行的原因是抛出安全——如果当你的代码在promise构造函数中运行时发生了你没有预料到的异常,它将变成拒绝,这种形式的抛出安全——将抛出的错误转换为拒绝是很重要的,有助于维护可预测的代码。

出于这个抛出安全的原因,承诺构造函数被选择而不是延迟(延迟是一种允许你正在做的事情的替代承诺构造方法)——至于最佳实践——我将传递元素并使用承诺构造函数:

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

出于这个原因——只要可以使用promise构造函数而不是导出函数——我建议您使用它。无论何时你可以避免两者,避免两者和链。

注意,你永远不应该在if(condition)这样的情况下使用promise构造函数,第一个例子可以这样写:

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

简单:

var promiseResolve, promiseReject;

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

promiseResolve();

helper方法可以减轻这种额外的开销,并给您同样的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
    });
});

我在2015年为我的框架提出了一个解决方案。我把这种类型的承诺称为任务

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


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

// resolve from outside
promise.resolve(200)