所有的javascript回调都是异步的吗?如果没有,我怎么知道哪些是?

时间:2013-09-29 21:16:37

标签: javascript node.js asynchronous synchronous

我很好奇所有的javascript回调是否都是异步的,或者只是在某些情况下是这种情况。此外,我确定是什么让javascript代码异步(或使用异步javascript的方法)在浏览器和nodejs之间有所不同,所以我想知道在每种情况下什么构成真正的异步javascript。

我的印象是,在下面的场景中,我实际上并没有编写异步代码。

function addOne(value){
  value = value + 1;
  return value;
}

function simpleMap(values, callback){
  for(i = 0; i < values.length; i++){
    val = values[i];
    val = callback(val);
    values[i] = val;
  }
  return values;
}

newValues = simpleMap([1,2,3], addOne);

但是,例如,我知道jQuery的AJAX函数是真正的异步(不考虑现在可用的承诺)。是什么让jQuery的AJAX异步?是否涉及XHR请求这么简单,在浏览器中,所有XHR请求都是异步的?

我对nodejs环境有同样的问题。节点中的某些东西只有在涉及文件i / o,process.nextTick,setTimeout或setInterval之类的东西时才能是异步的吗?为什么当我使用mongodb / mongoose进行数据库调用时,是异步的?幕后发生了怎样的事情呢?

环境是否预先确定异步“情况”?或者是否有一些方法可以使自己的函数真正异步而不利用环境的非常特定的函数(例如xhr,节点中的文件io,process.nexttick等)?

5 个答案:

答案 0 :(得分:40)

  

我很好奇所有的javascript回调是否都是异步的

没有。例如,Array#sort使用的回调不是异步的,也不是String#replace使用的回调。

您知道回调是否异步的唯一方法是来自其文档。通常,涉及外部资源请求(例如,ajax调用)的是异步的,而其他可能是也可能不是。

  

但是,例如,我知道jQuery的AJAX函数是真正异步的......

不一定,因为当前jQuery仍然有async标志,你可以设置false强制同步请求。 (这不是一个好主意,他们会删除它,但你可以 .jQuery将标志传递给提供同步/异步行为的底层浏览器对象。)

  

是什么让jQuery的AJAX异步?

浏览器。 jQuery的ajax调用使用XMLHttpRequest对象(或在某些情况下,script元素),默认为浏览器提供的异步操作。

  

或者有没有办法让自己的功能真正异步,而不需要利用环境的特定功能......

直到最近,没有。在第5版规范中,JavaScript 语言基本上对线程和异步性的整个概念保持沉默;只有当你进入环境时才会出现。使异步生成的唯一方法是使用主机提供的函数,例如NodeJS上的nextTick(或异步完成的任何操作)或浏览器上的setTimeout

在2015年6月的ECMAScript第6版规范中,他们将 promises 引入该语言。回调通过then连接到ES6承诺,并且总是异步调用(即使附加回调时承诺已经解决),因此JavaScript在语言中具有异步性现在水平。因此,如果您实现函数以便它返回一个promise而不是接受一个回调,那么您将知道连接到它的then回调将异步触发。

答案 1 :(得分:9)

要创建自己的异步函数,必须使用解释器可能提供的其他异步函数。

此代码例如定义了一个异步的函数“addKeyHandler”。但这只能起作用,因为JS引擎异步调用document.onKey。 JavaScript引擎能够提供异步功能,因为操作系统提供了JS所使用的功能。操作系统反过来只能提供异步功能,因为硬件提供它(称为硬件中断)。

但是,如果操作系统和硬件没有提供任何异步功能,那么仍然可以编写JS解释器。但它必须使用无限循环并检查每次迭代是否发生任何事件,然后调用适当的回调。这意味着CPU总是处于满负荷状态。

var keyCallbacks = [];

var addKeyHandler = function(f) {
  keyCallbacks.push(f);
};

document.onkeypress = function(e) {
  keyCallbacks.forEach(function(f) {
      f(e);
  });
};

addKeyHandler(function(e) {
    console.log(String.fromCharCode(e.charCode));
});

答案 2 :(得分:7)

您自己称之为的回调是常规函数调用,它们始终是同步的。

某些本机API(例如,AJAX,地理位置,Node.js磁盘或网络API)是异步的,稍后将在事件循环中执行它们的回调。

如果从异步回调中同步调用回调,它最终也会异步。

答案 3 :(得分:0)

  

简单地进行回调并不能使函数异步。有许多函数示例采用函数参数但不是异步的,例如,Array&#39; s forEach

     

对于异步函数,它需要执行异步操作。引入异步性的方法可以是

     
      
  • 计时器功能setTimeoutsetInterval
  •   
  • 特殊功能nextTicksetImmediate
  •   
  • 执行I / O(侦听网络,查询数据库,从资源读取或写入)
  •   
  • 订阅活动
  •   

根据文章"Does taking a callback make a function asynchronous?"

答案 4 :(得分:0)

一个简单的答案:

除非处理承诺,否则JS回调仅在依赖于JS外部的API(例如由浏览器提供)时才是异步的。

<SOAP-ENV:Body><ns1:Request><param0 xsi:type="xsd:string">null</param0><param1 xsi:type="xsd:string">null</param1></Request> setTimeout等是异步的,因为它们依赖于外部api。 (例如,fetchsetTimeout Web api的一部分,而windowOrGlobalWorker本身就是Web api。)

如果回调不依赖外部API,则它是同步的。

Promise是JS中唯一的本机异步功能。

了解所有情况的一种好方法是,至少阅读异步的MDN入门中的前几篇文章:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous