异步的起源

故事必须从头说起,在很久很久以前……

为校验表单,JavaScript 诞生了

在那个拨号上网的洪荒年代,浏览器还非常初级,与服务器进行数据交互的唯一方式就是提交表单。用户填写完成之后,交给服务器处理,如果内容合规当然好,如果不合规就麻烦了,必须打回来重填。那会儿网速还是论 Kb 的,比如我刚上网那会儿开始升级到 33.6Kb,主流还是 22.4Kb……

所以很容易想象:当用户填完100+选项,按下提交按钮,等待几十秒甚至几分钟之后,反馈回来的信息却是:“您的用户名不能包含大写字母”,他会有多么的崩溃多么的想杀人。为了提升用户体验,网景公司的布兰登·艾克大约用10天时间,开发出 JavaScript 的原型,从此,这门注定改变世界的语言就诞生了。

只是当时大家都还没有认识到这一点,发明它的目的,只是为校验表单。

JavaScript 中存在大量异步计算

同样为了提升用户体验,HTML DOM 也选择了边加载、边生成、边渲染的策略。再加上要等待用户操作,大量交互都以事件来驱动。于是,JavaScript 里很早就存在着大量的异步计算。

这也带来一个好处,作为一门 UI 语言,异步操作帮 JavaScript 避免了页面冻结。

为什么异步操作可以避免界面冻结呢?

同步的利弊

假设你去到一家饭店,自己找座坐下了,然后招呼服务员拿菜单来。

服务员说:“对不起,我是‘同步’服务员,我要服务完这张桌子才能招呼你。”

那一桌人明明已经吃上了,你只是想要菜单,这么小的一个动作,服务员却要你等待别人的一个大动作完成。你是不是很想抽ta?

这就是“同步”的问题:顺序交付的工作1234,必须按照1234的顺序完成。

不过“同步”也有“同步”的好处:逻辑非常简单。你不用担心每步操作会消耗多少时间,反正每一步操作都会在上一步完成之后才进行,只管往后写就是了。

异步的利弊

与之相反,异步,则是将耗时很长的 A 交付的工作交给系统之后,就去继续做 B 交付的工作。等到系统完成之后,再通过回调或者事件,继续做 A 剩下的工作。

从观察者的角度,看起来 AB 工作的完成顺序,和交付他们的时间顺序无关,所以叫“异步”。

那些需要大量计算(比如 Service Worker),或者复杂查询(比如 Ajax)的工作,JS 引擎把它们交给系统之后,就立刻返回继续待机了,于是再进行什么操作,浏览器也能第一时间响应,这让用户的感觉非常好。

有利必有弊,异步的缺点就是:必须通过特殊的语法才能实现,而这些语法就不如同步那样简单、清晰、明了。

异步计算的实现

异步计算有两种常见的实现形式。

事件侦听

这种形式在浏览器里比较常见,比如,我们可以对一个 <button> 的用户点击行为增加侦听,在点击事件触发后调用函数进行处理。

document.getElementById('#button').addEventListener('click', function (event) {
  // do something
}, false);

也可以使用 DOM 节点的 onclick 属性绑定侦听函数:

document.getElementById('#button').onclick = function (event) {
  // do something
}

回调

到了 Node.js(以及其它 Hybrid 环境),由于要和引擎外部的环境进行交互,大部分操作都变成回调。比如用 fs.readFile() 读取文件内容:

const fs = require('fs');

fs.readFile('path/to/file.txt', 'utf8', (err, content) => {
  if (err) {
    throw err;
  }
  console.log(content);
});

如果你不熟悉 Node.js 也没关系,jQuery 里也有类似的操作,最常见的就是侦听页面加载状态,加载完成后启动回调函数:

$(function () {
  // 绑定事件
  // 创建组件
  // 以及其它操作
});

results matching ""

    No results matching ""