javascript - 在外部解决承诺的 JavaScript“惯用”方式是什么?
问题描述
在一个程序中,我发现外部可解决承诺的概念(因为没有更好的词)很有用。用例是:程序中的多个位置需要某个值,但在一个特定位置(作为其他工作的一部分)异步计算。所以这个想法是预先定义一个全局变量来保存值的承诺:
让变量 = settlablePromise();
(其中 settlablePromise 是一个实用函数 - 见下文)。
无论在哪里需要值,我们都可以简单地 .then 或 await 变量(因为它已被分配了一个承诺)。
程序中计算值的地方将调用 variable.settle(x) 其中 x 是计算值。
这个方案有效,但我想知道是否有更标准的方法,而不是这个“settlablePromise”函数,如下所示。它的功能是产生一个可以在外部解决的承诺:
function settlablePromise()
{
let resolver = null;
let p = new Promise( (resolv,err) => {
resolver = resolv;
});
p.settle = function(v)
{
resolver(v);
};
return p;
}
解决方案
该方案有效,但我想知道是否有更标准的方法
很大程度上,答案是:不要。:-) 相反,这样做:
const variable = xyz();
...并制作xyz
可以完成获取值工作的代码部分,而不是variable.settle(x)
稍后调用该代码。暴露结算方法的问题是它们可以被任何代码调用,可能不止一次,但只有第一个调用会对承诺产生影响。因此,设计有意使那些对创建 Promise 的代码私有。如果没有启动一些可以报告完成的异步操作,则该承诺是没有意义的。
如果将执行异步工作的代码尚未准备好立即开始工作,您仍然可以xyz
返回一个承诺并让该代码稍后启动该过程。结算方法将对该代码保持私有,而不是暴露给所有代码,保持封装。
这是一个罕见的用例,通常您希望在创建 Promise 时开始工作。但在这种罕见的情况下,您可以在不暴露结算功能的情况下做到这一点。
例如:
// In a module specific to the asynchronous work
let [promise, start] = (() => {
let start;
const promise = new Promise((resolve, reject) => {
start = () => {
// ...Actually start the work, handle completion by calling
// `resolve` or `reject...
};
});
return [promise, start];
})();
export function xyz() { // The public face of the work
return promise;
}
// Code elsewhere in the module starts the process via `start`
你甚至可以直接暴露 Promise,而不是让它成为函数调用的结果:
// In a module specific to the asynchronous work
let [promiseOfNiftyStuff, start] = (() => {
let start;
const promise = new Promise((resolve, reject) => {
start = () => {
// ...Actually start the work, handle completion by calling
// `resolve` or `reject...
};
});
return [promise, start];
})();
export { promiseOfNiftyStuff };
// Code elsewhere in the module starts the process via `start`
同样,这是一个罕见的用例。
Yury Tarabanko提供了一个很好的例子来说明“罕见”用例,您需要将结算处理程序泄漏到包含的上下文中,并且仍然保持良好的封装(在本例中为fromEvent
):
async function* fromEvent(element, event) {
let resolve = null;
element.addEventListener(event, (event) => {
resolve(event);
});
while (true) {
yield new Promise(r => {
resolve = r;
})
}
}
async function run() {
const button = document.getElementById('test');
for await (const event of fromEvent(button, 'click')) {
console.log('clicked');
}
}
run();
<button id="test">Click</button>
推荐阅读
- asp.net - 隐藏按钮标签后面的CouponCode,并在点击按钮时显示在模型中
- excel - 如何获取 VLOOKUP 计数并总结查找结果
- c# - ASP.NET Core 3.1 InProcess 托管应用程序在启动时出现异常后未重新启动
- visual-studio - C# .NET Core 3.1 代码可以长时间使用 Visual Studio 执行,但发布的单个文件可执行文件会消耗大量内存
- javascript - 如果用户在字段中输入错误信息,则防止在字段中清除信息
- sql - 如何创建空的临时表?
- python - 在多列中为每次出现创建尽可能多的记录
- html - 带排水沟的 CSS 网格布局
- android - 不变违规:ViewPagerAndroid 已从 React Native 中删除 - IOS 上的错误
- shake-build-system - 启动服务的正确方法,如果它没有使用shake-build运行?