当函数返回时, 调用者能够拿到预期结果, 则称该函数是同步 (synchronous) 的, 反之称该函数是异步 (asynchronous) 的. 就是说, 在主线程内完成的任务是同步的, 主线程外产生额外线程的任务是异步的. 之前一直把这两个概念搞反了--

下面这篇文章有一个很形象的比喻:

JavaScript:彻底理解同步、异步和事件循环(Event Loop)

同步:
线程 A (主线程): 接下来要处理一个 ajax 请求.
线程 A: 发送请求. // 这时 A 被占用, 不能进行其他任务
线程 A: ... // 等待返回
线程 A: 请求成功返回了, 拿到返回的数据.
线程 A: 继续执行其余代码.

实际上的 ajax 线程是异步的.

异步:
线程 A (主线程): B, 这里有一个 ajax 请求, 我把参数给你了, 你去处理吧.
线程 B: OK. // 返回
线程 A: ... // 尚未得到预期结果, 继续执行其余代码
线程 B: ...
线程 A: ...
线程 B: 请求成功返回了.
线程 A: 好的. // 得到预期结果

异步和多线程的关系可以这么概括: 异步是目的, 多线程是异步的一种手段.

可见, 异步操作时, A 不能在调用函数返回时得到预期结果. 但如果在 A 中需要用到调用函数的返回结果怎么办呢? JavaScript 中的解决方法有若干程, 最常用的是同步回调:

线程 A (主线程): B, 这里有一个 ajax 请求, 我把参数和回调函数都给你了, 你去处理吧.
线程 B: OK. // 返回
线程 A: ... // 尚未得到预期结果, 继续执行其余代码
线程 B: ...
线程 A: ...
线程 B: 请求成功返回了.
/*
 * 按照回调的定义, 此时 A 会执行回调函数, 不过, A 有可能正在执行
 * 其他代码, 因此 B 会将返回作为一个事件放入事件队列.
 */
线程 A: 忙完手头的事情了.
线程 A: 我检查一下 B 有没有返回. // 检查事件队列的操作一直都在进行
线程 A: 检查到事件队列中有返回的事件, 所以先响应这个事件. // 执行回调函数

消息队列是先进先出的.

 


REFERENCE

JavaScript:彻底理解同步、异步和事件循环(Event Loop)