我有一个纯JavaScript承诺(内置实现或poly-fill):

var promise = new promise(函数(解析,拒绝){/*…* /});

从规范来看,Promise可以是:

" settle "和" resolved " “解决”和“拒绝” “等待”

我有一个用例,我希望同步审问承诺并确定:

承诺达成了吗? 如果是,承诺解决了吗?

我知道我可以使用#then()来安排在Promise改变状态后异步执行的工作。我不是在问你该怎么做。

这个问题是关于Promise状态的同步询问。我怎样才能做到这一点呢?


当前回答

在节点中,写入未记录的内部进程。

> 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> ]

其他回答

更新:2019

Bluebird.js提供了这个:http://bluebirdjs.com/docs/api/isfulfilled.html

var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());

如果您更喜欢创建自己的包装器,这里有一个关于它的不错的博客。

因为JavaScript是单线程的,所以很难找到一个足够常见的用例来证明将其放在规范中是正确的。知道承诺是否被解决的最佳位置是在.then()中。测试Promise是否被履行将创建一个轮询循环,这很可能是错误的方向。

如果你想同步推理异步代码,Async /await是一个很好的结构。

await this();
await that();
return 'success!';

另一个有用的调用是Promise.all()

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

当我第一次得到这个答案时,这就是我正在寻找的用例。

似乎没有人想出一个不需要任何技巧的最简单的解决方案:

定义一个变量来指示承诺正在运行 在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

不,没有同步API,但这是我的async承诺(在@Matthijs的帮助下):

函数promiseState(p) { Const t = {}; 回报的承诺。种族([p t]) .then(v => (v == t)?"pending": " completed ", () => "rejected"); } var a = Promise.resolve(); var b = Promise.reject(); var c = new Promise(() => {}); promiseState (a)。Then (state => console.log(state));/ /实现 promiseState (b)。Then (state => console.log(state));/ /拒绝 promiseState (c)。Then (state => console.log(state));/ /等待

2019:

据我所知,做到这一点的简单方法是thenable,超薄包装承诺或任何异步作业。

const sleep = (t) => new Promise(res => setTimeout(res,t));
const sleeping = sleep(30);

function track(promise){
    let state = 'pending';
    promise = promise.finally( _=> state ='fulfilled');
    return {
        get state(){return state},
        then: promise.then.bind(promise), /*thentable*/
        finally:promise.finally.bind(promise),
        catch:promise.catch.bind(promise),
    }
}


promise = track(sleeping);
console.log(promise.state) // pending

promise.then(function(){
    console.log(promise.state); // fulfilled
})

这里是一个更充实的QueryablePromise的es6版本,允许在第一次解析后进行链接和捕获,并立即解析或拒绝以保持api与本机Promise一致。

const PROMISE = Symbol('PROMISE') const tap = fn => x => (fn(x), x) const trace = label => tap(x => console.log(label, x)) class QueryablePromise { resolved = false rejected = false fulfilled = false catchFns = [] constructor(fn) { this[PROMISE] = new Promise(fn) .then(tap(() => { this.fulfilled = true this.resolved = true })) .catch(x => { this.fulfilled = true this.rejected = true return Promise.reject(x) }) } then(fn) { this[PROMISE].then(fn) return this } catch(fn) { this[PROMISE].catch(fn) return this } static resolve(x) { return new QueryablePromise((res) => res(x)) } static reject(x) { return new QueryablePromise((_, rej) => rej(x)) } } const resolvedPromise = new QueryablePromise((res) => { setTimeout(res, 200, 'resolvedPromise') }) const rejectedPromise = new QueryablePromise((_, rej) => { setTimeout(rej, 200, 'rejectedPromise') }) // ensure our promises have not been fulfilled console.log('test 1 before: is resolved', resolvedPromise.resolved) console.log('test 2 before: is rejected', rejectedPromise.rejected) setTimeout(() => { // check to see the resolved status of our promise console.log('test 1 after: is resolved', resolvedPromise.resolved) console.log('test 2 after: is rejected', rejectedPromise.rejected) }, 300) // make sure we can immediately resolve a QueryablePromise const immediatelyResolvedPromise = QueryablePromise.resolve('immediatelyResolvedPromise') // ensure we can chain then .then(trace('test 3 resolved')) .then(trace('test 3 resolved 2')) .catch(trace('test 3 rejected')) // make sure we can immediately reject a QueryablePromise const immediatelyRejectedPromise = QueryablePromise.reject('immediatelyRejectedPromise') .then(trace('test 4 resolved')) .catch(trace('test 4 rejected')) <script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>