我怎样才能同步确定JavaScript Promise的状态?

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

var promise = new Promise(function (resolve, reject) { /* ... */ });

从规范来看,一个承诺可以是下列之一:

  • “解决”和“解决”
  • “解决”和“拒绝”
  • “待定”

我有一个用例,我希望同步询问Promise并确定:

  • 是无极的解决?

  • 如果是的话,Promise是否解决了?

我知道我可以使用#then()来安排在Promise更改状态后asynchronous执行的工作。 我不是问如何做到这一点。

这个问题具体是关于对诺言状态的同步审问 。 我怎样才能做到这一点?

本地JavaScript承诺不存在这样的同步检查API。 用本地承诺来做这件事是不可能的。 规范没有规定这种方法。

Userland库可以做到这一点,如果你的目标是一个特定的引擎(如V8),并有权访问平台代码 (也就是说,你可以在核心编写代码),那么你可以使用特定的工具(如私人符号)来实现。 这是超级特定的,但不是在userland。

您可以通过Promise.resolve进行比赛
这不是同步的,而是现在发生

 function promiseState(p, isPending, isResolved, isRejected) { Promise.race([p, Promise.resolve('a value that p should not return')]).then(function(value) { if (value == 'a value that p should not return') { (typeof(isPending) === 'function') && isPending(); }else { (typeof(isResolved) === 'function') && isResolved(value); } }, function(reason) { (typeof(isRejected) === 'function') && isRejected(reason); }); } 

用于testing和理解asynchronous意义的小脚本

 var startTime = Date.now() - 100000;//padding trick "100001".slice(1) => 00001 function log(msg) { console.log((""+(Date.now() - startTime)).slice(1) + ' ' + msg); return msg;//for chaining promises }; function prefix(pref) { return function (value) { log(pref + value); return value; };} function delay(ms) { return function (value) { var startTime = Date.now(); while(Date.now() - startTime < ms) {} return value;//for chaining promises }; } setTimeout(log, 0,'timeOut 0 ms'); setTimeout(log, 100,'timeOut 100 ms'); setTimeout(log, 200,'timeOut 200 ms'); var p1 = Promise.resolve('One'); var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "Two"); }); var p3 = Promise.reject("Three"); p3.catch(delay(200)).then(delay(100)).then(prefix('delayed L3 : ')); promiseState(p1, prefix('p1 Is Pending '), prefix('p1 Is Resolved '), prefix('p1 Is Rejected ')); promiseState(p2, prefix('p2 Is Pending '), prefix('p2 Is Resolved '), prefix('p2 Is Rejected ')); promiseState(p3, prefix('p3 Is Pending '), prefix('p3 Is Resolved '), prefix('p3 Is Rejected ')); p1.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : ')); p2.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : ')); p3.catch(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : ')); log('end of promises'); delay(100)(); log('end of script'); 

结果与延迟(0)(评论时延)

 00001 end of promises 00001 end of script 00001 Level 1 : One 00001 Level 1 : Three 00001 p1 Is Resolved One 00001 p2 Is Pending undefined 00001 p3 Is Rejected Three 00001 Level 2 : One 00001 Level 2 : Three 00001 delayed L3 : Three 00002 Level 3 : One 00002 Level 3 : Three 00006 timeOut 0 ms 00100 timeOut 100 ms 00100 Level 1 : Two 00100 Level 2 : Two 00101 Level 3 : Two 00189 timeOut 200 ms 

并用firefox的这个testing的结果(chrome保持顺序)

 00000 end of promises 00100 end of script 00300 Level 1 : One 00300 Level 1 : Three 00400 p1 Is Resolved One 00400 p2 Is Pending undefined 00400 p3 Is Rejected Three 00400 Level 2 : One 00400 Level 2 : Three 00400 delayed L3 : Three 00400 Level 3 : One 00400 Level 3 : Three 00406 timeOut 0 ms 00406 timeOut 100 ms 00406 timeOut 200 ms 00406 Level 1 : Two 00407 Level 2 : Two 00407 Level 3 : Two 

promiseState make .race和.then:Level 2

不,没有同步的API,但这里是我的版本的asynchronouspromiseState (从@Matthijs的帮助):

 function promiseState(p) { const t = {}; return Promise.race([p, t]) .then(v => (v === t)? "pending" : "fulfilled", () => "rejected"); } var a = Promise.resolve(); var b = Promise.reject(); var c = new Promise(() => {}); promiseState(a).then(state => console.log(state)); // fulfilled promiseState(b).then(state => console.log(state)); // rejected promiseState(c).then(state => console.log(state)); // pending 

您可以在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; } 

这个基本的function缺失真的很烦人。 如果你使用的是node.js,那么我知道两个解决方法,他们都不是很漂亮。 下面的代码片段都实现了相同的API:

 > Promise.getInfo( 42 ) // not a promise { status: 'fulfilled', value: 42 } > Promise.getInfo( Promise.resolve(42) ) // fulfilled { status: 'fulfilled', value: 42 } > Promise.getInfo( Promise.reject(42) ) // rejected { status: 'rejected', value: 42 } > Promise.getInfo( p = new Promise(() => {}) ) // unresolved { status: 'pending' } > Promise.getInfo( Promise.resolve(p) ) // resolved but pending { status: 'pending' } 

似乎没有任何方法可以使用任何技巧来区分最后两个承诺状态。

1.使用V8debuggingAPI

这与util.inspect使用的是相同的技巧。

 const Debug = require('vm').runInDebugContext('Debug'); Promise.getInfo = function( arg ) { let mirror = Debug.MakeMirror( arg, true ); if( ! mirror.isPromise() ) return { status: 'fulfilled', value: arg }; let status = mirror.status(); if( status === 'pending' ) return { status }; if( status === 'resolved' ) // fix terminology fuck-up status = 'fulfilled'; let value = mirror.promiseValue().value(); return { status, value }; }; 

2.同步运行微任务

这样可以避免debuggingAPI,但通过使所有未完成的microtasks和process.nextTickcallback同步运行,会产生一些可怕的语义。 它也有防止检查承诺触发“未处理的承诺拒绝”错误的副作用。

 Promise.getInfo = function( arg ) { const pending = {}; let status, value; Promise.race([ arg, pending ]).then( x => { status = 'fulfilled'; value = x; }, x => { status = 'rejected'; value = x; } ); process._tickCallback(); // run microtasks right now if( value === pending ) return { status: 'pending' }; return { status, value }; }; 

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

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

你可以做什么,是使用一个variables来存储状态,手动设置状态到该variables,并检查该variables。

 var state = 'pending'; new Promise(function(ff, rjc) { //do something async if () {//if success state = 'resolved'; ff();// } else { state = 'rejected'; rjc(); } }); console.log(state);//check the state somewhere else in the code 

当然,这意味着您必须能够访问承诺的原始代码。 如果你不这样做,那么你可以这样做:

 var state = 'pending'; //you can't access somePromise's code somePromise.then(function(){ state = 'resolved'; }, function() { state = 'rejected'; }) console.log(state);//check the promise's state somewhere else in the code 

我的解决scheme是更多的编码,但是我认为你可能不需要为你使用的每一个承诺都这样做。

从Node.js版本8起,您现在可以使用智能检测软件包来同步检查原生承诺(没有任何危险的攻击)。

你可以添加一个方法到Promise.prototype。 它看起来像这样:

编辑:第一个解决scheme是不正常工作,像这里的大多数答案。 它返回“挂起”,直到asynchronous函数“.then”被调用,这不会立即发生。 (同样是关于使用Promise.race的解决scheme)。 我的第二个解决scheme解决这个问题。

 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; }; } 

你可以在任何Promise上使用它。 举个例子:

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

第二(和正确的)解决scheme:

 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; } } } }; } 

并使用它:

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

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

如果您正在使用ES7实验,则可以使用asynchronous方式来轻松地包装您要收听的承诺。

 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; } 

我写了一个npm包,promise-value,它提供了一个resolved标志的promise包装:

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

它也提供对promise值(或错误)的同步访问。 这不会改变Promise对象本身,遵循包装而不是延伸模式。

在节点中,说process.binding('util').getPromiseDetails(promise)