jok*_*yme 132 javascript promise es6-promise
我有一个纯JavaScript承诺(内置实现或poly-fill):
var promise = new Promise(function (resolve, reject) { /* ... */ });
根据规范,Promise可以是以下之一:
我有一个用例,我希望同步询问Promise并确定:
承诺落户了吗?
如果是这样,Promise是否已解决?
我知道我可以#then()用来安排Promise更改状态后异步执行的工作.我不是问怎么做.
这个问题具体是关于Promise状态的同步审讯.我怎样才能做到这一点?
Ben*_*aum 66
对于本机JavaScript承诺,不存在此类同步检查API.用本机承诺做这件事是不可能的.规范没有指定这样的方法.
Userland库可以做到这一点,如果您的目标是特定引擎(如v8)并且可以访问平台代码(也就是说,您可以在核心中编写代码),那么您可以使用特定工具(如私有符号)来实现此目的.这是超级特定的,而不是用户区.
0xa*_*xaB 31
promise-status-async可以解决问题.它是异步的,但它不会then用来等待承诺得到解决.
const {promiseStatus} = require('promise-status-async');
// ...
if (await promiseStatus(promise) === 'pending') {
const idle = new Promise(function(resolve) {
// can do some IDLE job meanwhile
});
return idle;
}
Run Code Online (Sandbox Code Playgroud)
jib*_*jib 19
不,没有同步API,但这是我的异步版本promiseState(在@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)); // pendingRun Code Online (Sandbox Code Playgroud)
小智 16
你可以用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);
});
}
Run Code Online (Sandbox Code Playgroud)
一个小脚本,用于测试和理解它们异步的含义
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');
Run Code Online (Sandbox Code Playgroud)
延迟结果(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
Run Code Online (Sandbox Code Playgroud)
和firefox的这个测试结果(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
Run Code Online (Sandbox Code Playgroud)
promiseState make .race和.then:Level 2
您可以在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
}
Run Code Online (Sandbox Code Playgroud)
await使用@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"
})();
Run Code Online (Sandbox Code Playgroud)
请注意,这个异步函数“几乎”像同步函数一样立即执行(或者实际上可能立即执行)。
我浏览了针对此问题提出的解决方案,但没有找到与我在 Node.js 中使用的简单方法相对应的解决方案。
我定义了一个简单的 PromiseMonitor 类,它将 Promise 作为其构造函数的单个参数,并具有一个字符串属性,.status该属性返回与 Promise 状态“pending”、“resolved”或“rejected”相对应的标准字符串值,以及四个布尔属性.pending、.resolved、.rejected和.error。仅当为 true并且拒绝回调传递了 Error 对象时,该属性.error才设置为 true 。.rejected
该类只是.then()在 Promise 被解决或拒绝时使用 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 };
};
Run Code Online (Sandbox Code Playgroud)
要监视 Promise 的状态,只需创建 PromiseMonitor 的实例,并将 Promise 作为参数传入,例如:
let promiseObject = functionThatReturnsAPromise();
let promiseMonitor = new PromiseMonitor( promiseObject );
Run Code Online (Sandbox Code Playgroud)
现在您可以同步检查 PromiseMonitor 的所有属性,它将跟踪原始 Promise 的状态。这是一个测试脚本,演示了正在监视的 Promise 的三种可能的解决方案。
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();
Run Code Online (Sandbox Code Playgroud)
你可以用这种方式包装你的承诺
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;
}
Run Code Online (Sandbox Code Playgroud)
Bluebird.js提供了以下功能:http : //bluebirdjs.com/docs/api/isfulfilled.html
var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());
Run Code Online (Sandbox Code Playgroud)
如果您想创建自己的包装器,那么这里是一个不错的博客。
因为JavaScript是单线程的,所以很难找到足够通用的用例来证明将其放入规范中是合理的。知道承诺是否得到解决的最佳位置是.then()。测试Promise是否已满,将创建一个轮询循环,这很可能是错误的方向。
如果您想同步推理异步代码,那么async / await是一个不错的构造。
await this();
await that();
return 'success!';
Run Code Online (Sandbox Code Playgroud)
另一个有用的调用是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"]
Run Code Online (Sandbox Code Playgroud)
当我第一次获得这个答案时,这就是我要寻找的用例。
缺少此基本功能确实很烦人。如果您使用的是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' }
Run Code Online (Sandbox Code Playgroud)
似乎没有任何方法可以使用任何一种技巧来区分最后两个承诺状态。
这是使用相同的把戏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 };
};
Run Code Online (Sandbox Code Playgroud)
这样可以避免使用调试API,但是会引起所有待处理的微任务和process.nextTick回调同步运行,因此具有令人恐惧的语义。它还具有防止被检查的承诺触发“未处理的承诺拒绝”错误的副作用。
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 };
};
Run Code Online (Sandbox Code Playgroud)
在节点中,说未记录的内部 process.binding('util').getPromiseDetails(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> ]
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
41572 次 |
| 最近记录: |