我有一个纯JavaScript承诺(内置实现或poly-fill):
var promise = new promise(函数(解析,拒绝){/*…* /});
从规范来看,Promise可以是:
" settle "和" resolved "
“解决”和“拒绝”
“等待”
我有一个用例,我希望同步审问承诺并确定:
承诺达成了吗?
如果是,承诺解决了吗?
我知道我可以使用#then()来安排在Promise改变状态后异步执行的工作。我不是在问你该怎么做。
这个问题是关于Promise状态的同步询问。我怎样才能做到这一点呢?
使用惯用的原型,等待@jib的答案的使用。
Object.defineProperty(Promise.prototype, "state", {
get: function(){
const o = {};
return Promise.race([this, o]).then(
v => v === o ? "pending" : "resolved",
() => "rejected");
}
});
// usage: console.log(await <Your Promise>.state);
(async () => {
console.log(await Promise.resolve(2).state); // "resolved"
console.log(await Promise.reject(0).state); // "rejected"
console.log(await new Promise(()=>{}).state); // "pending"
})();
注意,这个异步函数像同步函数一样“几乎”立即执行(或者实际上可能立即执行)。
似乎没有人想出一个不需要任何技巧的最简单的解决方案:
定义一个变量来指示承诺正在运行
在promise中添加.finally子句,将变量设置为false(可以在promise创建后的任何时间执行)
之后,在代码中检查上述变量是否为真或假,以查看Promise是否仍在运行。
如果你不只是想知道它是否完成了,那么除了.finally之外,还要添加.then和.catch子句,将变量设置为"resolved"或"rejected"。
唯一的缺点是,在添加子句时,状态变量不会立即(同步地)设置,以防承诺已经完成。因此,最好将其添加到创建承诺后最早的位置。
例子:
async function worker(){
// wait a very short period of time
await (new Promise(resolve => setTimeout(resolve, 100)))
//...
}
const w1=worker()
let w1_running=true
w1.finally( ()=> {w1_running=false});
//...
//Then check if it's running
(async ()=>{
while(true){
if (w1_running) {
console.log("Still Busy :(")
} else {
console.log("All done :)")
break
}
await (new Promise(resolve => setTimeout(resolve, 10)))
}
})()
// Note we need some async action started otherwise the event loop would never reach the code in the function `worker` or in the `.finally` clause
这是我使用的将来模式:(https://github.com/Smallscript-Corp)
启用同步和异步fn使用
使事件模式与异步行为统一
class XPromise extends Promise {
state = 'pending'
get settled() {return(this.state !== 'pending')}
resolve(v,...a) {
this.state = 'resolved'
return(this.resolve_(this.value = v,...a))
}
reject(e,...a) {
this.state = 'rejected'
return(this.reject_(this.value = (e instanceof Error) ? e : XPromise.Error(e),...a))
}
static Error(e) {const v = Error('value-rejected'); v.value = e; return(v)}
static Future(fn,...args) { // FactoryFn
let r,t,fv = new XPromise((r_,t_) => {r=r_;t=t_})
fv.resolve_ = r; fv.reject_ = t;
switch(typeof fn) {
case 'undefined': break; case 'function': fn(fv,...args); break;
default: fv.resolve(fn)
}
return(fv)
}
}
global.Future = XPromise.Future
然后你可以创建可以使用同步和异步函数来解决的未来价值实例;支持统一处理事件。
你可以用它来写一个像这样的模式:
async doSomething() {
// Start both - logically async-parallel
const fvIsNetworkOnLine = this.fvIsNetworkOnline
const fvAuthToken = this.fvAuthToken
// await both (order not critical since both started/queued above)
await fvAuthToken
await fvIsNetworkOnLine
// ... we can check the future values here if needed `fv.resolved`, `fv.state` etc
// ... do dependent workflow here ...
}
onNetworkOnLine(fIsOnline) {
// We utilize the `fv.settled` below, and use the event to `settle` it etc
if(fIsOnline) {
if(this.fvNetworkAvailable_)
this.fvNetworkAvailable_.resolve(true)
this.fvNetworkAvailable_ = undefined
}
else if(this.fvNetworkAvailable_.settled) {
this.fvNetworkAvailable_ = undefined
}
}
get fvNetworkAvailable() {
if(navigator.onLine)
return true
else if(this.fvNetworkAvailable_)
return this.fvNetworkAvailable_
return (this.fvNetworkAvailable_ = Future())
}
get fvAuthToken() {
if(this.fvAuthToken_)
return this.fvAuthToken_
const authTokenFv = async fv => {
// ... handle retry logic etc here ...
}
return(this.fvAuthToken_ = Future(authTokenFv))
}
警告:process.binding(“跑龙套”)。节点16上的getPromiseDetails未定义!
基准:
候选人:
/**
* https://stackoverflow.com/a/47009572/5318303
*/
const isPromisePending1 = (() => { // noinspection JSUnresolvedFunction
const util = process.binding('util') // noinspection JSUnresolvedFunction
return promise => !util.getPromiseDetails(promise)[0]
})()
/**
* https://stackoverflow.com/a/35852666/5318303
*/
const isPromisePending2 = (promise) => util.inspect(promise) === 'Promise { <pending> }'
/**
* https://stackoverflow.com/a/35820220/5318303
*/
const isPromisePending3 = (promise) => {
const t = {}
return Promise.race([promise, t])
.then(v => v === t, () => false)
}
测试的承诺:
const a = Promise.resolve()
const b = Promise.reject()
const c = new Promise(() => {})
const x = (async () => 1)()
运行基准:
const n = 1000000
console.time('isPromisePending1')
for (let i = 0; i < n; i++) {
isPromisePending1(a)
isPromisePending1(b)
isPromisePending1(c)
isPromisePending1(x)
}
console.timeEnd('isPromisePending1')
console.time('isPromisePending2')
for (let i = 0; i < n; i++) {
isPromisePending2(a)
isPromisePending2(b)
isPromisePending2(c)
isPromisePending2(x)
}
console.timeEnd('isPromisePending2')
console.time('isPromisePending3')
for (let i = 0; i < n; i++) {
await isPromisePending3(a)
await isPromisePending3(b)
await isPromisePending3(c)
await isPromisePending3(x)
}
console.timeEnd('isPromisePending3')
结果:
isPromisePending1: 440.694ms
isPromisePending2: 3.354s
isPromisePending3: 4.761s
显然isPromisePending1()太快了(8~10倍)!但它在节点16上不可用!(见上述警告)。
注意:此方法使用未文档化的Node.js内部构件,可以在没有警告的情况下进行更改。
在Node中,你可以使用process.binding('util')来同步确定promise的状态。getPromiseDetails(/* promise */);
这将返回:
[0,]表示未决,
[1, /* value */]表示已实现,或
[2, /* value */]表示拒绝。
const pending = new Promise(resolve => setTimeout(() => resolve('yakko')));;
const fulfilled = Promise.resolve('wakko');
const rejected = Promise.reject('dot');
[pending, fulfilled, rejected].forEach(promise => {
console.log(process.binding('util').getPromiseDetails(promise));
});
// pending: [0, ]
// fulfilled: [1, 'wakko']
// rejected: [2, 'dot']
将其包装到一个helper函数中:
const getStatus = promise => ['pending', 'fulfilled', 'rejected'][
process.binding('util').getPromiseDetails(promise)[0]
];
getStatus(pending); // pending
getStatus(fulfilled); // fulfilled
getStatus(rejected); // rejected