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

Posted

技术标签:

【中文标题】所有的javascript回调都是异步的吗?如果不是,我怎么知道哪些是?【英文标题】:Are all javascript callbacks asynchronous? If not, how do I know which are? 【发布时间】:2013-10-05 16:12:09 【问题描述】:

我很好奇是否所有 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、node 中的文件 io、process.nexttick 等)?

【问题讨论】:

【参考方案1】:

我很好奇是否所有的 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回调将被异步触发。

【讨论】:

【参考方案2】:

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

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

如果您从异步回调中同步调用回调,它最终也将是异步的。

【讨论】:

【参考方案3】:

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

此代码例如定义了一个异步函数“addKeyHandler”。但这只是因为 document.onKey 被 JS 引擎异步调用。 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));
);

【讨论】:

很好的答案!回调、操作系统和硬件之间的相似之处更容易理解。【参考方案4】:

简单地进行回调并不会使函数异步。有许多采用函数参数但不是异步的函数示例,例如ArrayforEach

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

定时器功能setTimeout, setInterval 特殊功能nextTick, setImmediate 执行 I/O(监听网络、查询数据库、读取或写入资源) 订阅事件

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

【讨论】:

【参考方案5】:

寻求一个简单的答案:

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

setTimeoutfetch 等是异步的,因为它们依赖于外部 api。 (例如,setTimeoutwindowOrGlobalWorker web api 的一部分,fetch 本身就是一个 web api。)

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

Promises 是 JS 中唯一的原生异步功能。

至少阅读 MDN 中关于异步的入门文章中的前几篇文章是了解这一切的好方法:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous

【讨论】:

以上是关于所有的javascript回调都是异步的吗?如果不是,我怎么知道哪些是?的主要内容,如果未能解决你的问题,请参考以下文章

Node.js Array.map() 是异步的吗?

译异步JavaScript的演变史:从回调到Promises再到Async/Await

JavaScript的异步编程

所有 Redis 命令都是异步的吗?

Promise 构造函数回调是异步执行的吗?

nodejs知识结构