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

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

从规范来看,Promise可以是:

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

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

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

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

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


当前回答

这里是一个更充实的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>

其他回答

对于原生JavaScript承诺,不存在这样的同步检查API。用本土的承诺是不可能做到的。规范没有指定这样的方法。

用户域库可以做到这一点,如果你的目标是一个特定的引擎(比如v8),并且可以访问平台代码(也就是说,你可以在核心中编写代码),那么你可以使用特定的工具(比如私有符号)来实现这一点。这是非常具体的,但不是在用户领域。

你可以这样包装你的承诺

function wrapPromise(promise) {
  var value, error,
      settled = false,
      resolved = false,
      rejected = false,
      p = promise.then(function(v) {
        value = v;
        settled = true;
        resolved = true;
        return v;
      }, function(err) {
        error = err;
        settled = true;
        rejected = true;
        throw err;
      });
      p.isSettled = function() {
        return settled;
      };
      p.isResolved = function() {
        return resolved;
      };
      p.isRejected = function() {
        return rejected;
      };
      p.value = function() {
        return value;
      };
      p.error = function() {
        return error;
      };
      var pThen = p.then, pCatch = p.catch;
      p.then = function(res, rej) {
        return wrapPromise(pThen(res, rej));
      };
      p.catch = function(rej) {
        return wrapPromise(pCatch(rej));
      };
      return p;
}

警告: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上不可用!(见上述警告)。

你可以在Promise.prototype中添加一个方法。它是这样的:

编辑:第一个解决方案不能正常工作,就像这里的大多数答案一样。它返回“pending”直到异步函数“。然后”被调用,这不是立即发生的。(使用Promise.race的解决方案也是如此)。我的第二个解决方案解决了这个问题。

if (window.Promise) {
    Promise.prototype.getState = function () {
        if (!this.state) {
            this.state = "pending";
            var that = this;
            this.then(
                function (v) {
                    that.state = "resolved";
                    return v;
                },
                function (e) {
                    that.state = "rejected";
                    return e;
                });
        }
        return this.state;
    };
}

你可以在任何承诺上使用它。为例:

myPromise = new Promise(myFunction);
console.log(myPromise.getState()); // pending|resolved|rejected

第二个(也是正确的)解决方案:

if (window.Promise) {
    Promise.stateable = function (func) {
        var state = "pending";
        var pending = true;
        var newPromise = new Promise(wrapper);
        newPromise.state = state;
        return newPromise;
        function wrapper(resolve, reject) {
            func(res, rej);
            function res(e) {
                resolve(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "resolved";
                    else
                        state = "resolved";
                    pending = false;
                }
            }
            function rej(e) {
                reject(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "rejected";
                    else
                        state = "rejected";
                    pending = false;
                }
            }
        }
    };
}

并使用它:

注意:在这个解决方案中,您不必使用“new”操作符。

myPromise = Promise.stateable(myFunction);
console.log(myPromise.state); // pending|resolved|rejected

您可以扩展Promise类来创建一个新的可查询的Promise 类。

您可以通过继承本机可用的Promise类来创建自己的子类,比如QueryablePromise,它的实例上有一个可用的status属性,您可以使用该属性同步查询Promise对象的状态。它的实现可以在下面看到,或者参考这个来获得更好的解释。

class QueryablePromise extends Promise { constructor (executor) { super((resolve, reject) => executor( data => { resolve(data) this._status = 'Resolved' }, err => { reject(err) this._status = 'Rejected' }, )) this._status = 'Pending' } get status () { return this._status } } // Create a promise that resolves after 5 sec var myQueryablePromise = new QueryablePromise((resolve, reject) => { setTimeout(() => resolve(), 5000) }) // Log the status of the above promise every 500ms setInterval(() => { console.log(myQueryablePromise.status) }, 500)