Javascript 异步函数外部堆栈上下文

Javascript 异步函数外部堆栈上下文,javascript,asynchronous,ecmascript-2017,Javascript,Asynchronous,Ecmascript 2017,有时,代码想知道某个特定函数或子函数是否正在运行。例如,node.js有一个适用于异步内容的函数,但不确定它是否包含异步函数 一些简单的代码可以解释我需要什么: inUpdate = true; try { doUpdate(); } finally { inUpdate = false; } 然后可以使用类似以下内容: function modifyThings() { if (inUpdate) throw new Error("Can't modify while updat

有时,代码想知道某个特定函数或子函数是否正在运行。例如,node.js有一个适用于异步内容的函数,但不确定它是否包含异步函数

一些简单的代码可以解释我需要什么:

inUpdate = true;
try {
  doUpdate();
} finally {
  inUpdate = false;
}
然后可以使用类似以下内容:

function modifyThings() {
  if (inUpdate) throw new Error("Can't modify while updating");
}
随着async的出现,如果doUpdate函数是异步的,则此代码将中断。当然,使用回调风格的函数已经是这样了

当然,可以对doUpdate函数进行修补,以在每次等待时维护变量,但即使您可以控制代码,这也很麻烦且容易出错,并且在尝试跟踪doUpdate内的异步函数调用时会中断

我尝试了monkey patching Promise。原型:

const origThen = Promise.prototype.then;
Promise.prototype.then = function(resolve, reject) {
  const isInUpdate = inUpdate;
  origThen.call(this, function myResolve(value) {
    inUpdate = isInUpdate;
    try {
      return resolve(value);
    } finally {
      inUpdate = false;
    }
  }, reject);
}
不幸的是,这不起作用。我不知道为什么,但异步继续代码最终运行在解析调用堆栈之外,可能使用了一个微任务

请注意,仅仅做到以下几点是不够的:

function runUpdate(doUpdate) {
  inUpdate = true;
  doUpdate.then(() => inUpdate = false).catch(() => inUpdate = false);
}
原因是:

runUpdate(longAsyncFunction);
console.log(inUpdate); // incorrectly returns true
是否有任何方法可以从异步函数外部跟踪某个对象,以便能够判断该函数是否已调用或其任何子代调用是否正在运行

我知道用生成器模拟异步函数是可能的,在这种情况下,我们可以控制调用堆栈,因为我们可以调用gen.next,但异步函数的出现只是解决了这个难题,因此,我特别寻找一种与本机而不是Babel生成的异步函数一起工作的解决方案

编辑:澄清问题:假设此代码是异步函数的调用方,外部代码是否有办法知道异步函数的特定调用是否正在运行或是否已挂起。它是否正在运行将由最终由堆栈中某个异步函数调用的函数决定

编辑:进一步澄清:预期功能与node.js中的相同,但也适用于浏览器。域已经可以使用承诺来工作,因此异步函数可能也可以工作,而不是经过测试

[是否]可以判断调用的函数或其任何子代调用是否正在运行


对。答案总是否定的,因为一次只运行一段代码。Javascript根据定义是单线程的

不要把事情弄得太复杂了。如果doUpdate返回的承诺与异步函数类似,请等待:

inUpdate = true;
try {
  await doUpdate();
//^^^^^
} finally {
  inUpdate = false;
}
您还可以使用:


这就像同步代码一样,在函数调用或其任何子代运行时,inUpdate==true。当然,只有当异步函数在完成它的工作之前没有解决承诺时,这才有效。如果您觉得只应在doUpdate函数的某些特定部分设置inUpdate标志,那么是的,该函数将需要维护标志本身,就像同步代码一样。

此代码允许我在一定程度上做我想做的事情:

函数安装异步跟踪{ /*全球承诺:真实*/ 如果Promise.isAsyncTracker抛出新错误“只能安装一个跟踪器”; const RootPromise=Promise.isAsyncTracker?Promise.RootPromise:Promise; 让主动=真; 常数跟踪器={ trackf,o,…args{ const prevObj=tracker.trackObj; tracker.trackObj=o; 试一试{ 返回f.applythis,args; }最后{ tracker.trackObj=prevObj; } }, trackObj:未定义, 卸载{ 主动=假; 如果Promise===AsyncTrackPromise.prevPromise返回; 如果承诺!==AsyncTrackPromise返回; Promise=AsyncTrackPromise.prevPromise; } }; AsyncTrackPromise.prototype=Object.createPromise; AsyncTrackPromise.rootPromise=rootPromise; AsyncTrackPromise.prevPromise=承诺; 承诺=承诺; AsyncTrackPromise.resolve=值=>{ 返回新的AsyncTrackPromiseSolve=>resolvevalue; }; AsyncTrackPromise.reject=val=>{ 返回新的AsyncTrackPromiserSolve,reject=>rejectvalue; }; AsyncTrackPromise.all=iterable=>{ const promises=Array.fromiterable; if!promises.length返回AsyncTrackPromise.resolve; 返回新的AsyncTrackPromiseSolve,拒绝=>{ 让拒绝=错误; 让结果=新的Arraypromises.length; 让done=0; const allPromises=promises.mappromise=>{ 如果promise&&typeof promise.then==“函数”{ 回报承诺; } 返回新的AsyncTrackPromise.resolvepromise; }; allPromises.forEachpromise,ix=>{ promise.thenvalue=>{ 如果被拒绝返回; 结果[ix]=数值; 完成++; 如果完成===results.length{ 解决结果; } },原因=>{ 如果被拒绝返回; 拒绝=正确; 拒绝理由; }; }; }; }; AsyncTrackPromise.race=iterable=>{ const promises=Array.fromiterable; if!promissions.length返回新的AsyncTrackPromission=>{}; 返回新的AsyncTrackPromiseSolve,拒绝=>{ 让我们决定=错误; if promises.somepromise=>{ 如果!promise | | promise.then的类型!=“函数”{ 承诺; 返回true; } }返回; promises.forEachpromise,ix=>{ promise.thenvalue=>{ 如果决定返回; 已解决=正确; 解析值; },原因=>{ 如果决定返回; 已解决=正确; 拒绝理由; }; }; }; }; 函数AsyncTrackPromisehandler{ const promise=新的RootPromisehandler; promise.trackObj=tracker.trackObj; promise.origThen=promise.then; 允诺。然后=超越; promise.origCatch=promise.catch; promise.catch=catchOverride; 如果你答应,最后{ promise.originfinally=promise.finally; promise.finally=最终超越; } 回报承诺; } AsyncTrackPromise.isAsyncTracker=true; 函数,然后重写解决、拒绝{ const trackObj=this.trackObj; if!active | | trackObj==未定义返回this.origThen.applythis,参数; 返回此.origThen.call 这 myResolvertrackObj,resolve, 拒绝&&myResolvertrackObj,拒绝 ; } 函数catchOverrideObject{ const trackObj=this.trackObj; if!active | | trackObj==未定义返回this.origCatch.catch.applythis,参数; 返回此.origCatch.call 这 myResolvertrackObj,拒绝 ; } 函数finallyOverridecallback{ const trackObj=this.trackObj; if!active | | trackObj==未定义返回this.origCatch.catch.applythis,参数; 返回此.origCatch.call 这 myResolvertrackObj,拒绝 ; } 返回跟踪器; 函数myResolvertrackObj,resolve{ 返回函数myResolveval{ 如果trackObj==未定义{ 返回resolval; } RootPromise.resolve.then=>{ const prevObj=tracker.trackObj; tracker.trackObj=trackObj; RootPromise.resolve.then=>{ tracker.trackObj=prevObj; }; }; const prevObj=tracker.trackObj; tracker.trackObj=trackObj; 试一试{ 返回resolval; }最后{ tracker.trackObj=prevObj; } }; } } tracker=installAsyncTrack; 函数trackfunc,值,…args{ 返回tracker.trackfunc,{value},value,…args; } 函数showhere,哪个{ log'At call',其中'from',其值为:',tracker.trackObj&&tracker.trackObj.value; } 异步函数testwhich,sub{ show1,其中; 等待delayMath.random*100; show2,其中; 如果sub==‘解析’{ 等待承诺。解决测试“sub”; show3,其中; } 如果sub=='调用'{ 等待testwhich+‘sub’; show3,其中; } } 函数延迟{ 返回新PromiseSolve=>setTimeoutresolve,毫秒; } 跟踪测试,“测试1”; 跟踪测试,“测试2”; tracktest、'test3'、'resolve';
tracktest、test4、call;这并没有回答这个问题,我不知道你为什么要提到JS是单线程的,因为它在这里似乎不相关。@DDS它非常相关,JavaScript已经运行到完成语义。您不想知道这些函数是否正在运行,您似乎想知道它们是否在排队。@DDS不正确,它们总是运行到yield、return或块的末尾。总是避免您所说的确切场景是他们拒绝添加协同路由的原因。@DDS那么为什么//错误地返回true?承诺是一个悬而未决的状态,正如我们一直试图告诉您的,在JavaScript中没有暂停执行这样的事情。即使是在发电机里也不行,尽管这是最接近的东西。当然不是在异步函数中。我投票决定结束这件事。你的问题仍然没有任何意义。同样,您是否想知道异步调用是否已排队?这不起作用,因为从函数外部调用时,在doUpdate函数返回/解析之前,inUpdate标志也将为true。@DDS您所说的从外部调用是什么意思,直到doUpdate函数完全返回您想要的内容为止?你需要它的目的是什么?我目前需要它来跟踪函数所做的更改,而不是其他函数所做的更改,而不需要函数的配合。因此,如果一个函数调用makeChangenewData,我需要将它赋给正确的调用方。@DDS这是为-track调用您的人制作的调用堆栈。只要使用新的Error.stack,它甚至可以与异步函数一起工作。不幸的是,没有更好的API用于调用堆栈。无法知道哪一行与函数匹配,因为我无法提前知道将传入哪个函数。即使我做了,我也不知道是哪张发票
例如,这对跟踪更改很重要。我知道这是一个非常古老的注释,但是对于子调用,上面的输出应该是什么样的?他们似乎在被叫之前就失去了我的背景?我已经放弃了。请注意在一定程度上的免责声明。我现在正在寻找一个编译器解决方案,它将通过显式管理上下文来维护上下文。请注意,作为一种语言特性,存在堆栈上下文的抖动。不过我现在没有链接。它还没有准备好使用。
var inUpdate = true;
doUpdate().finally(() => {
  inUpdate = false;
});