你的问题很复杂,我可能会在某些方面弄错。
但这里有 3-4 个想法可能会有所帮助。
想法 1
从“then”开始,您可以立即使用 Proxy 调用“handler”,它几乎禁止所有操作。
完成此操作后,您只需注意函数退出或抛出错误。
通过这种方式,您可以跟踪返回的值是否以任何方式实际使用。
但是,如果不使用返回的值 - 您将看不到它。
所以这允许这种用途:
... some code ...
await myPromiser(); // << notice the return value is ignored
... some more code ...
如果这对您来说是个问题,那么这种方法只能起到部分作用。
但如果这是一个比你上次调用的问题 (let e = myPromiser(...)) 也没有用,因为之后可以忽略“e”。
下面,在此答案的末尾,javascript 代码成功区分了您的三种情况
想法 2
您可以在调用“thirdPartyCode”代码之前使用 Babel 对其进行检测。
如果需要,Babel 也可以在运行时使用。
有了它,您可以:
2.1 找出myPromise的所有用法并检查它是否合法。
2.2 在每次 await 或 '.then' 之后添加对某些标记函数的调用 - 这样您就可以使用选项 1 检测所有情况。
答案 3
如果您正在寻找一种方法来了解 Promise 是属于您的还是已解决 - 那么答案是“没有这样的方法”。
证明(以Chrome为例):
let p = new Promise((resolve, reject)=>{
console.log('Code inside promise');
resolve(5);
});
p.then(()=>{
console.log('Code of then')
})
console.log('Code tail');
// Executed in Chrome:
// Code inside promise
// Code tail
// Code of then
这告诉我们解析代码总是在当前调用上下文之外执行。
IE。我们可能一直期望从 Promise 内部调用 'resolve' 会导致立即调用所有订阅的函数,
但事实并非如此 - v8 将等到当前函数执行结束,然后才执行 then 处理程序。
想法 4(部分)
如果您想拦截对 SystemPromise.then 的所有调用并确定您的 Promiser 是否被调用 - 有一种方法:您可以用您的实现覆盖 Promise.then。
不幸的是,这不会告诉您异步功能是否结束。我已经尝试过使用它 - 请参阅下面我的代码中的 cmets。
答案 1 的代码:
let mySymbol = Symbol();
let myPromiserRef = undefined;
const errorMsg = 'ANY CUSTOM MESSAGE HERE';
const allForbiddingHandler = {
getPrototypeOf: target => { throw new Error(errorMsg); },
setPrototypeOf: target => { throw new Error(errorMsg); },
isExtensible: target => { throw new Error(errorMsg); },
preventExtensions: target => { throw new Error(errorMsg); },
getOwnPropertyDescriptor: target => { throw new Error(errorMsg); },
defineProperty: target => { throw new Error(errorMsg); },
has: target => { throw new Error(errorMsg); },
get: target => { throw new Error(errorMsg); },
set: target => { throw new Error(errorMsg); },
deleteProperty: target => { throw new Error(errorMsg); },
ownKeys: target => { throw new Error(errorMsg); },
apply: target => { throw new Error(errorMsg); },
construct: target => { throw new Error(errorMsg); },
};
// We need to permit some get operations because V8 calls it for some props to know if the value is a Promise.
// We tell it's not to stop Promise resolution sequence.
// We also allow access to our Symbol prop to be able to read args data
const guardedHandler = Object.assign({}, allForbiddingHandler, {
get: (target, prop, receiver) => {
if(prop === mySymbol)
return target[prop];
if(prop === 'then' || typeof prop === 'symbol')
return undefined;
throw new Error(errorMsg);
},
})
let myPromiser = (...args)=> {
let vMyPromiser = {[mySymbol]:[...args] };
return new Proxy(vMyPromiser,guardedHandler);
// vMyPromiser.proxy = new Proxy(vMyPromiser,guardedHandler);
// vMyPromiser.then = ()=> {
// myPromiserRef = vMyPromiser;
// console.log('myPromiserThen - called!');
// return vMyPromiser.proxy;
// }
// return vMyPromiser;
};
let someArg = ['someArgs1', 'someArgs2'];
const someoneElsesPromiserB = async(a)=>{
return a;
}
const someoneElsesPromiserD = async(a)=>{
return a;
}
async function thirdPartyCode(a) {
console.log('CODE0001')
if (a == 1) {
console.log('CODE0002')
return myPromiser(a, someArg) // can allow and act on this
}
console.log('CODE0003')
let b = await someoneElsesPromiserB(a)
console.log('CODE0004')
if (b == 2) {
console.log('CODE0005')
let c = await myPromiser(a, someArg) // must error on this
console.log('CODE0006')
let x = c+5; // <= the value should be used in any way. If it's not - no matter if we did awaited it or not.
console.log('CODE0007')
}
console.log('CODE0008')
let d = await someoneElsesPromiserD(a);
console.log('CODE0009')
let e = myPromiser(a, someArg) // note no await
console.log('CODE0010')
return e // can allow and act on this
};
// let originalThen = Promise.prototype.then;
// class ReplacementForPromiseThen {
// then(resolve, reject) {
// // this[mySymbol]
// if(myPromiserRef) {
// console.log('Trapped then myPromiser - resolve immediately');
// resolve(myPromiserRef.proxy);
// myPromiserRef = undefined;
// } else {
// console.log('Trapped then other - use System Promise');
// originalThen.call(this, resolve, reject);
// }
// }
// }
//
// Promise.prototype.then = ReplacementForPromiseThen.prototype.then;
(async()=>{
let r;
console.log('Starting test 1');
r = await thirdPartyCode(1);
console.log('Test 1 finished - no error, args used in myPromiser = ', r[mySymbol]);
console.log("\n\n\n");
console.log('Starting test 3');
r = await thirdPartyCode(3);
console.log('Test 3 finished - no error, args used in myPromiser = ', r[mySymbol]);
console.log("\n\n\n");
console.log('Starting test 2 - should see an error below');
r = await thirdPartyCode(2);
})();