使用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));