首先,这是一个非常具体的情况,故意以错误的方式将异步调用改造到非常同步的代码库中,该代码库有数千行之长,目前时间无法提供进行更改以“正确执行”的能力。它伤害了我身体的每一根纤维,但现实和理想往往不相符。我知道这很糟糕。

好了,我要怎么做才能:

function doSomething() {

  var data;

  function callBack(d) {
    data = d;
  }

  myAsynchronousCall(param1, callBack);

  // block here and return data when the callback is finished
  return data;
}

示例(或缺少示例)都使用库和/或编译器,这两者都不适用于此解决方案。我需要一个具体的例子,如何使它块(例如不离开doSomething函数,直到回调被调用)而不冻结UI。如果这样的事情在JS中是可能的。


当前回答

"不要告诉我我应该怎么做"正确的方式"之类的"

好的。但你真的应该用正确的方式去做……或者其他

“我需要一个具体的例子来说明如何让它成为块……不冻结用户界面。如果这样的事情在JS是可能的。”

不,如果不阻塞UI,就不可能阻塞正在运行的JavaScript。

由于缺乏信息,很难提供解决方案,但一种选择可能是让调用函数执行一些轮询来检查全局变量,然后让回调函数将数据设置为全局变量。

function doSomething() {

      // callback sets the received data to a global var
  function callBack(d) {
      window.data = d;
  }
      // start the async
  myAsynchronousCall(param1, callBack);

}

  // start the function
doSomething();

  // make sure the global is clear
window.data = null

  // start polling at an interval until the data is found at the global
var intvl = setInterval(function() {
    if (window.data) { 
        clearInterval(intvl);
        console.log(data);
    }
}, 100);

所有这些都假设您可以修改doSomething()。我不知道有没有可能。

如果它可以修改,那么我不知道为什么你不只是传递一个回调到doSomething()从另一个回调调用,但我最好在陷入麻烦之前停止。;)


哦,管他呢。你给出的例子表明它可以正确地完成,所以我将展示这个解决方案……

function doSomething( func ) {

  function callBack(d) {
    func( d );
  }

  myAsynchronousCall(param1, callBack);

}

doSomething(function(data) {
    console.log(data);
});

因为您的示例包含一个传递给异步调用的回调,正确的方法是将一个函数传递给doSomething(),以便从回调调用。

当然,如果这是回调唯一做的事情,你只需要直接传递func…

myAsynchronousCall(param1, func);

其他回答

你想要的现在实际上是可能的。如果你可以在service worker中运行异步代码,在web worker中运行同步代码,那么你可以让web worker向service worker发送同步XHR,当service worker执行异步操作时,web worker的线程将等待。这不是一个很好的方法,但它可以工作。

promise的这种能力包括同步操作的两个关键特性,如下所示(or then()接受两个回调)。 当您获得结果时,调用resolve()并传递最终结果。 如果出现错误,调用reject()。

其思想是将结果通过.then()处理程序链传递。

const synchronize = (() => {
    let chain = Promise.resolve()
    return async (promise) => {
        return chain = chain.then(promise)
    }
})()

你也可以把它转换成回调。

function thirdPartyFoo(callback) {    
  callback("Hello World");    
}

function foo() {    
  var fooVariable;

  thirdPartyFoo(function(data) {
    fooVariable = data;
  });

  return fooVariable;
}

var temp = foo();  
console.log(temp);

使用Node 16的工作线程实际上使这成为可能,下面的例子是主线程正在运行异步代码,而工作线程正在同步地等待它。

这并不是说它非常有用,但它至少模糊地完成了同步等待异步代码所提出的最初问题。

const {
    Worker, isMainThread, parentPort, receiveMessageOnPort
} = require('worker_threads');
if (isMainThread) {
    const worker = new Worker(__filename);
    worker.on('message', async () => {
        worker.postMessage(await doAsyncStuff());
    });
} else {
    console.log(doStuffSync());
}

function doStuffSync(){
    parentPort.postMessage({fn: 'doStuff'});
    let message;
    while (!message) {
        message = receiveMessageOnPort(parentPort)
    }
    return message;
}

function doAsyncStuff(){
    return new Promise((resolve) => setTimeout(() => resolve("A test"), 1000));
}

"不要告诉我我应该怎么做"正确的方式"之类的"

好的。但你真的应该用正确的方式去做……或者其他

“我需要一个具体的例子来说明如何让它成为块……不冻结用户界面。如果这样的事情在JS是可能的。”

不,如果不阻塞UI,就不可能阻塞正在运行的JavaScript。

由于缺乏信息,很难提供解决方案,但一种选择可能是让调用函数执行一些轮询来检查全局变量,然后让回调函数将数据设置为全局变量。

function doSomething() {

      // callback sets the received data to a global var
  function callBack(d) {
      window.data = d;
  }
      // start the async
  myAsynchronousCall(param1, callBack);

}

  // start the function
doSomething();

  // make sure the global is clear
window.data = null

  // start polling at an interval until the data is found at the global
var intvl = setInterval(function() {
    if (window.data) { 
        clearInterval(intvl);
        console.log(data);
    }
}, 100);

所有这些都假设您可以修改doSomething()。我不知道有没有可能。

如果它可以修改,那么我不知道为什么你不只是传递一个回调到doSomething()从另一个回调调用,但我最好在陷入麻烦之前停止。;)


哦,管他呢。你给出的例子表明它可以正确地完成,所以我将展示这个解决方案……

function doSomething( func ) {

  function callBack(d) {
    func( d );
  }

  myAsynchronousCall(param1, callBack);

}

doSomething(function(data) {
    console.log(data);
});

因为您的示例包含一个传递给异步调用的回调,正确的方法是将一个函数传递给doSomething(),以便从回调调用。

当然,如果这是回调唯一做的事情,你只需要直接传递func…

myAsynchronousCall(param1, func);