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

好了,我要怎么做才能:

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中是可能的。


当前回答

看看JQuery的承诺:

http://api.jquery.com/promise/

http://api.jquery.com/jQuery.when/

http://api.jquery.com/deferred.promise/

重构代码:


    var dfd = new jQuery.Deferred();


    function callBack(data) {
       dfd.notify(data);
    }

    // do the async call.
    myAsynchronousCall(param1, callBack);

    function doSomething(data) {
     // do stuff with data...
    }

    $.when(dfd).then(doSomething);


其他回答

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

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

“我需要一个具体的例子来说明如何让它成为块……不冻结用户界面。如果这样的事情在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);

如果稍微调整一下需求,您希望实现的想法就可以实现

如果您的运行时支持ES6规范,则可以使用下面的代码。

更多关于异步函数的信息

async function myAsynchronousCall(param1) {
    // logic for myAsynchronous call
    return d;
}

function doSomething() {

  var data = await myAsynchronousCall(param1); //'blocks' here until the async call is finished
  return data;
}

看看JQuery的承诺:

http://api.jquery.com/promise/

http://api.jquery.com/jQuery.when/

http://api.jquery.com/deferred.promise/

重构代码:


    var dfd = new jQuery.Deferred();


    function callBack(data) {
       dfd.notify(data);
    }

    // do the async call.
    myAsynchronousCall(param1, callBack);

    function doSomething(data) {
     // do stuff with data...
    }

    $.when(dfd).then(doSomething);


人们可能不会考虑的一件事是:如果你控制了异步函数(其他代码段依赖于它),并且它所采用的代码路径不一定是异步的,你可以通过创建一个可选参数使它成为同步的(而不破坏其他代码段)。

目前:

async function myFunc(args_etcetc) {
    // you wrote this
    return 'stuff';
}

(async function main() {
    var result = await myFunc('argsetcetc');
    console.log('async result:' result);
})()

考虑:

function myFunc(args_etcetc, opts={}) {
    /*
        param opts :: {sync:Boolean} -- whether to return a Promise or not
    */
    var {sync=false} = opts;
    if (sync===true)
        return 'stuff';
    else
        return new Promise((RETURN,REJECT)=> {
            RETURN('stuff');
        });
}


// async code still works just like before:
(async function main() {
    var result = await myFunc('argsetcetc');
    console.log('async result:', result);
})();
// prints: 'stuff'

// new sync code works, if you specify sync mode:
(function main() {
    var result = myFunc('argsetcetc', {sync:true});
    console.log('sync result:', result);
})();
// prints: 'stuff'

当然,如果异步函数依赖于固有的异步操作(网络请求等),这是行不通的,在这种情况下,努力是徒劳的(没有有效地等待无理由的空闲旋转)。

另外,根据传入的选项返回值或Promise也是相当难看的。

("Why would I have written an async function if it didn't use async constructs?" one might ask? Perhaps some modalities/parameters of the function require asynchronicity and others don't, and due to code duplication you wanted a monolithic block rather than separate modular chunks of code in different functions... For example perhaps the argument is either localDatabase (which doesn't require await) or remoteDatabase (which does). Then you could runtime error if you try to do {sync:true} on the remote database. Perhaps this scenario is indicative of another problem, but there you go.)

在http://taskjs.org/上有一个不错的解决方案

它使用了javascript的新生成器。所以目前大多数浏览器都没有实现它。我在firefox中测试了它,对我来说,这是包装异步函数的好方法。

下面是来自GitHub项目的示例代码

var { Deferred } = task;

spawn(function() {
    out.innerHTML = "reading...\n";
    try {
        var d = yield read("read.html");
        alert(d.responseText.length);
    } catch (e) {
        e.stack.split(/\n/).forEach(function(line) { console.log(line) });
        console.log("");
        out.innerHTML = "error: " + e;
    }

});

function read(url, method) {
    method = method || "GET";
    var xhr = new XMLHttpRequest();
    var deferred = new Deferred();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if (xhr.status >= 400) {
                var e = new Error(xhr.statusText);
                e.status = xhr.status;
                deferred.reject(e);
            } else {
                deferred.resolve({
                    responseText: xhr.responseText
                });
            }
        }
    };
    xhr.open(method, url, true);
    xhr.send();
    return deferred.promise;
}