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

Creating a (ES6) promise without starting to resolve it

本文关键字:开始 解决 ES6 一个 创建 承诺      更新时间:2023-09-26

使用ES6 promise,如何在不定义解析逻辑的情况下创建promise?下面是一个基本示例(一些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);
}

我能看到的唯一方法是将解析函数存储在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构造函数是抛出安全的,并将异常转换为拒绝

有时它不适合,因此解析器会同步运行。以下是关于这个主题的相关阅读。

我想在这里加2美分。正是考虑到"创建es6 Promise而不启动解决它"这一问题,我解决了创建包装器函数并调用包装器函数的问题。代码:

假设我们有一个函数f,它返回一个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'),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助手,但即使如此,也坚持将延迟对象与承诺本身分离。。。

一个更全面的方法怎么样?

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

您可能会选择将构造函数命名为Deferred,这一术语在[javascript promise的历史]中具有很大的优先级。

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

通过返回一个装饰过的舞会而不是一个普通的对象,除了装饰之外,承诺的所有自然方法/属性都可以使用。

此外,通过处理fn,如果您需要/选择在Deferred上使用遮瑕图案,遮瑕图案仍然可用。

演示

现在,有了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();
}

我想,让这类问题看起来复杂的是Javascript的程序性。为了解决这个问题,我创建了一个简单的类。以下是它的外观:

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允许您在外部解决您的承诺,但这是一个反模式,因为它破坏了Promise封装模型。(现场演示)

import CPromise from "c-promise2";
const promise = new CPromise(() => {});
promise.then((value) => console.log(`Done: ${value}`)); //123
setTimeout(() => promise.resolve(123));