我有一个纯JavaScript承诺(内置实现或poly-fill):
var promise = new promise(函数(解析,拒绝){/*…* /});
从规范来看,Promise可以是:
" settle "和" resolved "
“解决”和“拒绝”
“等待”
我有一个用例,我希望同步审问承诺并确定:
承诺达成了吗?
如果是,承诺解决了吗?
我知道我可以使用#then()来安排在Promise改变状态后异步执行的工作。我不是在问你该怎么做。
这个问题是关于Promise状态的同步询问。我怎样才能做到这一点呢?
注意:此方法使用未文档化的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
使用惯用的原型,等待@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"
})();
注意,这个异步函数像同步函数一样“几乎”立即执行(或者实际上可能立即执行)。
如果你正在使用ES7实验版,你可以使用async轻松地包装你想要收听的承诺。
async function getClient() {
let client, resolved = false;
try {
client = await new Promise((resolve, reject) => {
let client = new Client();
let timer = setTimeout(() => {
reject(new Error(`timeout`, 1000));
client.close();
});
client.on('ready', () => {
if(!resolved) {
clearTimeout(timer);
resolve(client);
}
});
client.on('error', (error) => {
if(!resolved) {
clearTimeout(timer);
reject(error);
}
});
client.on('close', (hadError) => {
if(!resolved && !hadError) {
clearTimeout(timer);
reject(new Error("close"));
}
});
});
resolved = true;
} catch(error) {
resolved = true;
throw error;
}
return client;
}
在节点中,写入未记录的内部进程。
> process.binding('util').getPromiseDetails(Promise.resolve({data: [1,2,3]}));
[ 1, { data: [ 1, 2, 3 ] } ]
> process.binding('util').getPromiseDetails(Promise.reject(new Error('no')));
[ 2, Error: no ]
> process.binding('util').getPromiseDetails(new Promise((resolve) => {}));
[ 0, <1 empty item> ]
似乎没有人想出一个不需要任何技巧的最简单的解决方案:
定义一个变量来指示承诺正在运行
在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