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

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

从规范来看,Promise可以是:

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

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

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

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

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


当前回答

我写了一个小的npm包promise-value,它提供了一个带有解析标志的promise包装器:

https://www.npmjs.com/package/promise-value

它还提供了对承诺值(或错误)的同步访问。这不会改变Promise对象本身,而是遵循wrap而不是extend模式。

其他回答

你可以在Node.js中使用一个(丑陋的)黑客,直到一个本地方法被提供:

util = require('util');

var promise1 = new Promise (function (resolve) {
}

var promise2 = new Promise (function (resolve) {

    resolve ('foo');
}

state1 = util.inspect (promise1);
state2 = util.inspect (promise2);

if (state1 === 'Promise { <pending> }') {

    console.log('pending'); // pending
}

if (state2 === "Promise { 'foo' }") {

    console.log ('foo') // foo
}

你可以这样包装你的承诺

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

我浏览了这个问题的解决方案,没有找到一个与我在Node.js中使用的简单方法相对应的解决方案。

我已经定义了一个简单的类PromiseMonitor,它将承诺作为其构造函数的单个参数,并具有一个字符串属性.status,它返回与承诺状态对应的标准字符串值,“pending”,“resolved”或“rejected”,以及四个布尔属性.pending, .resolved, .rejected和.error。只有当.rejected为true并且拒绝回调被传递一个Error对象时,属性. Error才被设置为true。

该类只是在promise上使用.then()来在promise被解决或拒绝时更改PromiseMonitor的状态。它不会干扰原始承诺的任何其他使用。代码如下:

class PromiseMonitor {
    constructor(prm){
        this._status = "pending";
        this._pending = true;
        this._resolved = false;
        this._rejected = false;
        this._error = false;
        prm
            .then( ()=>{  
                        this._status = "resolved"; 
                        this._resolved = true; 
                        this._pending = false; 
                    } 
                , (err)=>{ 
                        this._status = "rejected";
                        this._pending = false;
                        this._rejected = true;
                        this._error = err instanceof Error ? true: false ; 
                    } 
                );
    }

    get status(){ return this._status; };
    get pending(){ return this._pending; };
    get resolved(){ return this._resolved; };
    get rejected(){ return this._rejected; };
    get error(){ return this._error };
};

要监视Promise的状态,只需创建一个PromiseMonitor实例,将Promise作为参数传入,例如:

let promiseObject = functionThatReturnsAPromise();
let promiseMonitor = new PromiseMonitor( promiseObject );

现在您可以同步检查promiseMonitor的所有属性,它将跟踪原始承诺的状态。下面是一个测试脚本,它演示了正在监视的承诺的三种可能的解决方案。

let ticks = 0;
let tickerID = setInterval( ()=>{++ticks; console.log(`..tick ${ticks}`)}, 1000);

async function run(){
    console.log("Start");

    let delay = prmDelay(2000);
    let delayMonitor = new PromiseMonitor(delay);

    // normal handling of delay promise
    delay.then((result)=>( console.log("Normal resolution of delay using .then()") ) );

    console.log("delay at start:\n", delay);
    console.log("delayMonitor at start:\n", delayMonitor);
    await delay;
    console.log("delay finished:\n", delay);
    console.log("delayMonitor finished:\n", delayMonitor);


    console.log("\n\n TEST2: Rejection without an Error test ================================")
    let rejDelay = prmDelay(3000, "reject");
    let rejMonitor = new PromiseMonitor(rejDelay);

    // normal handling of reject result on promise
    rejDelay.then((result)=>( console.log("Normal resolution of rejDelay using .then will not happen") ) 
                    , (err)=>( console.log("Rejection of rejDelay handled using .then")));

    console.log("rejDelay at start:\n", rejDelay);
    console.log("rejMonitor at start:\n", rejMonitor);

    await rejDelay.catch( (err)=>{ console.log( "Caught error using .catch on rejDelay" ); });

    console.log("rejDelay finished:\n", rejDelay);
    console.log("rejMonitor finished:\n", rejMonitor);


    console.log("\n\n TEST3: Rejection with an Error test ================================")
    let errMonitor ;
    let errDelay;
    try{

        errDelay = prmDelay(1000, "error");
        errMonitor = new PromiseMonitor(errDelay);
        
        // normal handling of results of the original promise
        errDelay.then(
            (result)=>{ 
                console.log("Normal expiry of errDelay");
                console.log("Monitor Status is " + errMonitor.status )
            } 
            , (err)=>{
                console.log("** Rejection of errDelay handled using .then()");
                console.log("   Monitor Status is " + errMonitor.status )
            }
        );

        console.log("errDelay at start:\n", errDelay);
        console.log("errMonitor at start:\n", errMonitor);

        await errDelay;

        console.log("**** This should never be run");

    } catch(err) { 

        console.log( "** Caught error on errDelay using try{}catch{}:" ); 
        console.log( "   Monitor Status is " + errMonitor.status )

    };

    console.log("errDelay finished:\n", errDelay);
    console.log("errMonitor finished:\n", errMonitor);
    

    clearInterval(tickerID);


}

/**
 * Creates a new promise with a specific result
 * @param {*} tt 
 * @param {*} exitType ("resolve", "reject" or "error")
 */
function prmDelay (tt, exitType) {
    
    return new Promise(function(resolve, reject) {
        if( exitType == 'reject' ){
            setTimeout(()=>{ reject("REJECTED")}, tt);
        } else if( exitType== 'error'){
            setTimeout(()=>{ reject(new Error( "ERROR Rejection") ); }, tt);
        } else {
            setTimeout(()=>{ resolve("RESOLVED") }, tt);
        } ;
    });
};


run();

老问题有很多答案,但似乎没有一个建议我认为是最简单的解决方案:在承诺解决/拒绝上设置bool指示器。

类Promise2 { 构造函数(args) { let promise = new promise(…args); promise.then(() => promise。_resolved_ = true); promise.catch(() => promise。_rejected_ = true); 返回的诺言; } } let p = new promise (r => setTimeout(r, 3000)); setInterval(() => { Console.log('正在同步检查p是否已解析?”,p._resolved_); }, 1000);