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

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


当前回答

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

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上获得。

其他回答

这里的许多答案与本文最后一个示例类似。 我正在缓存多个promise, 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();

and

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

如何创建一个函数劫持拒绝并返回它?

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

我想分享一些不同的东西,这是这个话题的延伸。

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

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

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

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

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

创建一个处理器函数:

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

将承诺发送给创建的处理程序

  switch (method) {
    case 'get': {
      this.handleHttpPromise(apiService.get(url));
      break;
    }
    case 'post': {
      if (jsonData) {
        this.handleHttpPromise(apiService.post(url, jsonData));
      }
      break;
    }
    // (...)
  }

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

如果你绑定并捕获Deferred对象,那么它完全实现了Promise API,你可以将它视为Promise并等待它等等。

⚠️ Editor's Note: I don't recommend this kind of pattern anymore since at the time of writing, Promise.prototype.finally was not a thing yet, then it became a thing… This could happen to other methods so I recommend you augment the promise instance with resolve and reject functions instead: function createDeferredPromise() { let resolve let reject const promise = new Promise((thisResolve, thisReject) => { resolve = thisResolve reject = thisReject }) return Object.assign(promise, {resolve, reject}) } Go upvote someone else's answer.

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