403

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

这工作正常,但有没有更简单的方法来做到这一点?如果没有,这是一个好习惯吗?

4

24 回答 24

201

简单的:

var promiseResolve, promiseReject;

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

promiseResolve();
于 2016-03-17T21:44:52.527 回答
133

在这里聚会有点晚了,但另一种方法是使用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
})
于 2016-01-06T16:02:37.973 回答
121

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

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

出于这个抛出安全的原因,promise 构造函数被选择而不是 deferreds(这是一种替代的 promise 构造方式,它确实允许你正在做的事情) - 至于最佳实践 - 我会传递元素并使用 promise 构造函数来代替:

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

出于这个原因——只要你可以使用 promise 构造函数而不是导出函数——我建议你使用它。只要你可以避免两者 - 避免两者并连锁。

请注意,您永远不应该将 promise 构造函数用于类似的事情if(condition),第一个示例可以写成:

var p = Promise[(someCondition)?"resolve":"reject"]();
于 2014-10-01T20:53:39.220 回答
28

我喜欢@JonJaques 的回答,但我想更进一步。

如果你先绑定然后then对象,那么它完全实现了API,你可以把它当作 Promise 等等。catchDeferredPromiseawait

⚠️ 编者注:我不再推荐这种模式,因为在撰写本文时,Promise.prototype.finally它还不是一个东西,然后它变成了一个东西……这可能发生在其他方法上,所以我建议你用resolvereject函数来增加 Promise 实例反而:

function createDeferredPromise() {
  let resolve
  let reject

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

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

去点赞别人的答案。

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.finally = this._promise.finally.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();

于 2017-11-04T15:13:19.477 回答
27

我在 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)
于 2017-07-28T00:42:33.063 回答
18

接受的答案是错误的。使用范围和引用非常容易,尽管它可能会让 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
于 2019-08-07T01:43:41.870 回答
13

辅助方法将减轻这种额外的开销,并给您相同的 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
    });
});
于 2018-02-03T00:58:13.397 回答
11

我正在使用辅助函数来创建我所谓的“平面承诺” -

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 包,代码也可以在 GitHub 上找到

于 2018-11-19T11:36:59.267 回答
9

您可以将 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.
于 2019-01-28T22:38:30.937 回答
9

这里的许多答案与本文中的最后一个示例相似。我正在缓存多个 Promise,resolve()andreject()函数可以分配给任何变量或属性。结果,我可以使这段代码更紧凑:

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

这是一个使用此版本的简化示例,defer()FontFace加载 Promise 与另一个异步进程结合起来:

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 

更新:如果您想封装对象,有两种选择:

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();
于 2019-06-11T15:47:44.743 回答
8

在某些情况下,我发现自己也错过了延迟模式。你总是可以在 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);
    }
}
于 2019-10-29T15:48:32.667 回答
5

以防万一有人来寻找简化此任务的 util 的打字稿版本:

export const deferred = <T>() => {
  let resolve!: (value: T | PromiseLike<T>) => void;
  let reject!: (reason?: any) => void;
  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });

  return {
    resolve,
    reject,
    promise,
  };
};

这可以用于例如。像:

const {promise, resolve} = deferred<string>();

promise.then((value) => console.log(value)); // nothing

resolve('foo'); // console.log: foo

于 2021-09-02T09:42:29.120 回答
3

我们的解决方案是使用闭包来存储 resolve/reject 函数,并附加一个函数来扩展 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');
于 2017-01-12T02:59:42.320 回答
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>

我希望这个答案有用!

于 2019-05-18T16:55:05.540 回答
2

感谢所有在此线程中发布的人。我创建了一个模块,其中包括前面描述的 Defer() 对象以及基于它构建的一些其他对象。它们都利用 Promise 和简洁的 Promise 回调语法在程序中实现通信/事件处理。

  • Defer:可以远程解决的 Promise 失败(在其主体之外)
  • 延迟:在给定时间后自动解决的承诺
  • TimeOut:在给定时间后自动失败的 Promise。
  • Cycle:可重新触发的 Promise 以使用 Promise 语法管理事件
  • Queue:基于 Promise 链的执行队列。

rp = require("openpromise")

https://github.com/CABrouwers/openpromise https://www.npmjs.com/package/openpromise

于 2020-05-01T03:35:36.233 回答
1

我为此写了一个小库。https://www.npmjs.com/package/@inf3rno/promise.exposed

我使用了其他人写的工厂方法方法,但我也覆盖了then, catch,finally方法,因此您也可以通过这些方法解决原始承诺。

在没有外部执行者的情况下解决 Promise:

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.");
于 2018-10-31T11:27:42.040 回答
1

我创建了一个名为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

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

于 2019-08-03T08:26:42.127 回答
1

类版本,在 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)
    }
}
于 2022-02-17T13:19:02.837 回答
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();
于 2019-03-23T14:35:25.517 回答
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();
  });
  ...
});
于 2019-05-27T18:51:15.000 回答
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'
于 2020-05-12T09:30:54.860 回答
0

由于我没有找到我想要的东西,所以当我结束这个问题时,我将分享我真正想要实现的目标。

场景:我有 3 个具有相同可能响应的不同 API,因此我想在单个函数中处理 Promise 的完成和错误处理。这就是我所做的:

  1. 创建一个处理函数:
  private handleHttpPromise = (promise: Promise<any>) => {
    promise
      .then((response: any) => {
        // do something with the response
        console.log(response);
      })
      .catch((error) => {
        // do something with the error
        console.log(error);
      });
  };
  1. 将您的承诺发送给创建的处理程序
  switch (method) {
    case 'get': {
      this.handleHttpPromise(apiService.get(url));
      break;
    }
    case 'post': {
      if (jsonData) {
        this.handleHttpPromise(apiService.post(url, jsonData));
      }
      break;
    }
    // (...)
  }
于 2020-11-28T22:23:54.153 回答
0

我想分享一些不同的东西,这个主题的扩展。

有时,您希望“任务承诺”在解决时自动在同一地址(属性或变量)重新创建。可以创建一个这样做的外部解析器。

带有外部解析器的重复承诺示例。每当调用解析器时,都会在相同的地址/变量/属性处创建一个新的 Promise。

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++;
  }
})();

https://jsfiddle.net/h3zvw5xr

于 2021-02-25T09:44:15.133 回答
-3

首先在浏览器或节点上启用 --allow-natives-syntax

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

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

于 2019-07-26T00:45:43.910 回答