同步调用异步Javascript函数

2020/10/12 09:21 · javascript ·  · 0评论

首先,这是一个非常特殊的情况,它以错误的方式故意将异步调用改型为一个非常同步的代码库,该代码库长成千上万行,并且当前时间不具备进行更改的能力。对的。” 它伤害了我的每一个生命,但是现实和理想常常并没有融合。我知道这很糟糕。

好的,顺便说一句,我该如何做,这样我可以:

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中有这种可能。

“不要告诉我应该怎么做”正确的方式”或其他方式”

好。但您确实应该以正确的方式来做...

“我需要一个具体的示例来说明如何使其阻塞...而无需冻结UI。如果在JS中可能发生这种情况。”

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

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

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);

异步函数,特征在ES2017,通过使用补充异步代码外表同步承诺(异步的特定代码的形式)和await关键字。还要注意在代码示例下面的关键字async前面的function关键字之前,关键字表示async / await函数。如果await没有关键字预先固定的功能,关键字将无法工作async因为当前没有例外,这意味着没有顶级等待将起作用(顶级等待意味着任何功能之外的等待)。尽管有关于高层await建议

ES2017已于2017年6月27日批准(即最终确定)为JavaScript的标准。Async await可能已在您的浏览器中运行,但是如果没有,您仍然可以使用像babeltraceur这样的javascript编译器来使用该功能Chrome 55完全支持异步功能。因此,如果您使用的是更新的浏览器,则可以尝试以下代码。

请参阅kangax的es2017兼容性表以了解浏览器兼容性。

这是一个示例async await函数doAsync,该函数需要三秒钟的暂停,并在从开始时间开始的每个暂停之后打印时差:

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

async function doAsync () {
  var start = Date.now(), time;
  console.log(0);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
  time = await doSomethingAsync();
  console.log(time - start);
}

doAsync();

当将await关键字放置在promise值之前(在这种情况下,promise值是函数doSomethingAsync返回的值),await关键字将暂停函数调用的执行,但不会暂停任何其他函数,并且它将继续执行其他代码,直到承诺解决为止。在promise解析后,它将解开promise的值,您可以认为await和promise表达式现在已被该未包装的值替换。

因此,由于await只是暂停,然后等待等待,然后在执行其余行之前取消包装值,因此可以在for循环和内部函数调用中使用它,如以下示例所示,该示例收集数组中等待的时间差并打印出数组。

function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this calls each promise returning function one after the other
async function doAsync () {
  var response = [];
  var start = Date.now();
  // each index is a promise returning function
  var promiseFuncs= [doSomethingAsync, doSomethingAsync, doSomethingAsync];
  for(var i = 0; i < promiseFuncs.length; ++i) {
    var promiseFunc = promiseFuncs[i];
    response.push(await promiseFunc() - start);
    console.log(response);
  }
  // do something with response which is an array of values that were from resolved promises.
  return response
}

doAsync().then(function (response) {
  console.log(response)
})

异步函数本身会返回一个Promise,因此您可以像在上面或另一个异步await函数中所做的那样,将其用作与链接的Promise。

如果您想同时发送请求,可以使用Promise.all来使用上面的函数,等待每个响应之后再发送另一个请求

// no change
function timeoutPromise (time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(Date.now());
    }, time)
  })
}

// no change
function doSomethingAsync () {
  return timeoutPromise(1000);
}

// this function calls the async promise returning functions all at around the same time
async function doAsync () {
  var start = Date.now();
  // we are now using promise all to await all promises to settle
  var responses = await Promise.all([doSomethingAsync(), doSomethingAsync(), doSomethingAsync()]);
  return responses.map(x=>x-start);
}

// no change
doAsync().then(function (response) {
  console.log(response)
})

如果promise可能拒绝,则可以将其包装在try catch中,也可以跳过try catch,然后将错误传播到async / await函数catch调用。您应该注意不要遗漏诺言错误,尤其是在Node.js中。以下是一些示例,展示了错误的工作原理。

function timeoutReject (time) {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      reject(new Error("OOPS well you got an error at TIMESTAMP: " + Date.now()));
    }, time)
  })
}

function doErrorAsync () {
  return timeoutReject(1000);
}

var log = (...args)=>console.log(...args);
var logErr = (...args)=>console.error(...args);

async function unpropogatedError () {
  // promise is not awaited or returned so it does not propogate the error
  doErrorAsync();
  return "finished unpropogatedError successfully";
}

unpropogatedError().then(log).catch(logErr)

async function handledError () {
  var start = Date.now();
  try {
    console.log((await doErrorAsync()) - start);
    console.log("past error");
  } catch (e) {
    console.log("in catch we handled the error");
  }
  
  return "finished handledError successfully";
}

handledError().then(log).catch(logErr)

// example of how error propogates to chained catch method
async function propogatedError () {
  var start = Date.now();
  var time = await doErrorAsync() - start;
  console.log(time - start);
  return "finished propogatedError successfully";
}

// this is what prints propogatedError's error.
propogatedError().then(log).catch(logErr)

如果您去此处,则可以看到有关即将发布的ECMAScript版本的最终建议。

仅ES2015(ES6)可以使用的替代方法是使用包装生成器函数的特殊函数。生成器函数具有一个yield关键字,该关键字可用于与周围函数复制await关键字。yield关键字和generator函数具有更多通用性,并且可以完成其他功能,而不仅仅是async await函数。如果你想,可以用来复制异步发电机的功能封装等待我想看看co.js顺便说一下,co的函数很像异步await函数返回一个promise。老实说,尽管此时浏览器对生成器功能和异步功能的兼容性几乎相同,所以如果您只希望异步等待功能,则应使用不带co.js的异步功能。

现在,除IE之外,当前所有主流浏览器(Chrome,Safari和Edge)中的异步功能(截至2017年)实际上对浏览器的支持都不错。

看一下JQuery Promises:

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

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

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

重构代码:


    var dfd = new jQuery.Deferred();


    函数callBack(data){
       dfd.notify(data);
    }

    //进行异步调用。
    myAsynchronousCall(param1,callBack);

    函数doSomething(data){
     //处理数据...
    }

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


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;
}

可以强制NodeJS中的异步JavaScript与sync-rpc同步

不过,它肯定会冻结您的UI,因此,对于是否可以采用所需的快捷方式,我仍然是反对者。即使NodeJS有时允许您阻止它,也无法在JavaScript中挂起“唯一线程”。在您的诺言得到解决之前,没有回调,事件,任何异步的东西都将无法处理。因此,除非读者遇到像OP这样的不可避免的情况(或者,就我而言,这是在编写没有回调,事件等的美化的shell脚本),否则请不要这样做!

但是,这是您可以执行的操作:

./calling-file.js

var createClient = require('sync-rpc');
var mySynchronousCall = createClient(require.resolve('./my-asynchronous-call'), 'init data');

var param1 = 'test data'
var data = mySynchronousCall(param1);
console.log(data); // prints: received "test data" after "init data"

./my-asynchronous-call.js

function init(initData) {
  return function(param1) {
    // Return a promise here and the resulting rpc client will be synchronous
    return Promise.resolve('received "' + param1 + '" after "' + initData + '"');
  };
}
module.exports = init;

局限性:

这些都是sync-rpc滥用的实现方式的结果require('child_process').spawnSync

  1. 在浏览器中将无法使用。
  2. 函数的参数必须可序列化。您的参数将传入和传出JSON.stringify,因此函数和不可枚举的属性(例如原型链)将丢失。

您想要的实际上已经成为可能。如果可以在Service Worker中运行异步代码,而在Web Worker中运行同步代码,则可以让Web Worker向Service Worker发送同步XHR,而当Service Worker执行异步操作时,Web Worker可以线程将等待。这不是一个很好的方法,但是它可以工作。

您也可以将其转换为回调。

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

function foo() {    
  var fooVariable;

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

  return fooVariable;
}

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

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

如果您的运行时支持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;
}
本文地址:http://javascript.askforanswer.com/tongbudiaoyongyibujavascripthanshu.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!