创建一个(ES6)承诺而不开始解决它

Creating a (ES6) promise without starting to resolve it

使用 ES6 承诺,如何在不定义解析逻辑的情况下创建承诺?这是一个基本示例(一些 TypeScript):

var promises = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return promises[key];
  }
  var promise = new Promise(resolve => {
    // But I don't want to try resolving anything here :(
  });

  promises[key] = promise;
  return promise;
}

function resolveWith(key: string, value: any): void {
  promises[key].resolve(value); // Not valid :(
}

使用其他 promise 库可以轻松完成。 JQuery例如:

var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = $.Deferred();    
  deferreds[key] = def;
  return def.promise();
}

function resolveWith(key: string, value: any): void {
  deferreds[key].resolve(value);
}

我能看到的唯一方法是将 resolve 函数存储在 promise 的执行程序中的某个地方,但这看起来很乱,而且我不确定它是在什么时候定义的 运行 - 是否总是 运行 立即施工?

谢谢。

好问题!

传递给 promise 构造函数的解析器有意同步运行以支持此用例:

var deferreds = [];
var p = new Promise(function(resolve, reject){
    deferreds.push({resolve: resolve, reject: reject});
});

然后,在稍后的某个时间点:

 deferreds[0].resolve("Hello"); // resolve the promise with "Hello"

给出promise构造函数的原因是:

  • 通常(但不总是)解析逻辑绑定到创建。
  • promise 构造函数是抛出安全的并将异常转换为拒绝。

有时它不适合,因此解析器同步运行。

更全面的方法如何?

您可以编写一个 returns 一个用 .resolve().reject() 方法修饰的新 Promise 的构造函数。

您可能会选择将构造函数命名为 Deferred - 在 javascript 承诺的 [历史] 中具有很多优先级的术语。

function Deferred(fn) {
    fn = fn || function(){};

    var resolve_, reject_;

    var promise = new Promise(function(resolve, reject) {
        resolve_ = resolve;
        reject_ = reject;
        fn(resolve, reject);
    });

    promise.resolve = function(val) {
        (val === undefined) ? resolve_() : resolve_(val);
        return promise;//for chainability
    }
    promise.reject = function(reason) {
        (reason === undefined) ? reject_() : reject_(reason);
        return promise;//for chainability
    }
    promise.promise = function() {
        return promise.then(); //to derive an undecorated promise (expensive but simple).
    }

    return promise;
}

通过返回装饰的 promsie 而不是普通对象,除了装饰之外,所有 promise 的自然 methods/properties 仍然可用。

此外,通过处理 fn,如果您 need/choose 在 Deferred 上使用它,揭示者模式仍然可用。

DEMO

现在,有了 Deferred() 实用程序,您的代码实际上与 jQuery 示例相同。

var deferreds = {};
function waitFor(key: string): Promise<any> {
  if (key in promises) {
    return deferreds[key].promise();
  }
  var def = Deferred();    
  deferreds[key] = def;
  return def.promise();
}

我想在这里加上我的 2 美分。考虑到问题“Creating a es6 Promise without starting resolve it”我解决了它创建一个包装函数并改为调用包装函数。代码:

假设我们有一个函数 f,它 return 是一个 Promise

/** @return Promise<any> */
function f(args) {
   return new Promise(....)
}

// calling f()
f('hello', 42).then((response) => { ... })

现在,我想准备调用f('hello', 42)而不实际解决它:

const task = () => f('hello', 42) // not calling it actually

// later
task().then((response) => { ... })

希望这会对某人有所帮助:)


如果我想准备,请按照评论中的要求引用Promise.all()(并由@Joe Frambach 回答)调用 f1('super')f2('rainbow'),return 承诺

的 2 个函数
const f1 = args => new Promise( ... )
const f2 = args => new Promise( ... )

const tasks = [
  () => f1('super'),
  () => f2('rainbow')
]

// later
Promise.all(tasks.map(t => t()))
  .then(resolvedValues => { ... })

JavaScript 土地上的情况正在慢慢好转,但这是一个情况,事情仍然不必要地复杂。这是一个简单的助手来公开解析和拒绝函数:

Promise.unwrapped = () => {
  let resolve, reject, promise = new Promise((_resolve, _reject) => {
    resolve = _resolve, reject = _reject
  })
  promise.resolve = resolve, promise.reject = reject
  return promise
}

// a contrived example

let p = Promise.unwrapped()
p.then(v => alert(v))
p.resolve('test')

显然曾经有一个 Promise.defer 助手,但即便如此,它也坚持将延迟对象与承诺本身分开......

让这类问题看起来复杂的原因是 Javascript 我猜的程序性质。为了解决这个问题,我创建了一个简单的 class。外观如下:

class PendingPromise {

    constructor(args) {
        this.args = args;
    }

    execute() {
        return new Promise(this.args);
    }
}

这个承诺只会在你调用execute()时执行。例如:

function log() {
    return new PendingPromise((res, rej) => {
        console.log("Hello, World!");
    });
}

log().execute();

CPomise allows you to resolve your promises outside, but this is an antipattern since it breaks Promise incapsulation model. (Live demo)

import CPromise from "c-promise2";

const promise = new CPromise(() => {});

promise.then((value) => console.log(`Done: ${value}`)); //123

setTimeout(() => promise.resolve(123));