很多人学 JavaScript 的时候,都会突然意识到一点
明明代码写在前面,为什么后面的先执行?明明发了请求,为什么拿不到数据?
从一段奇怪的输出说起
先看一段代码:
js
javascript
console.log('start');
setTimeout(() => {
console.log('222');
}, 1000);
console.log('end');
我觉得输出应该是start→ 222→ end,但实际运行起来是:
sql
start
end
222
222却跑到最后一次。
JS为什么是单线程?
要理解这件事,得先知道 JS 是怎么设计的。
Java、C++这类语言支持多线程------可以同时跑好几条运行路线,效率高,但写起来复杂,要手动处理各种竞争、锁、死锁的问题。
JS 的设计者当年做了一个取舍:让 JS 简单,单线程。一次只做一件事,从上到下顺序执行,不用操心多线程带来的那些麻烦。
这对写界面来说其实是相当合理的。大多数代码执行起来很快,压根不需要方程。
但问题来了------网络请求要等,计时器要等,用户点击也要等,这些"等"的事情,单线程总不能真的傻坐在那等吧?
事件循环:JS的"排队机制"
JS 的解决方案称为Event Loop(事件循环) 。
规则很简单:
- 同步代码,直接执行,从上到下,一行不落
- 遇到异步任务(
setTimeout、fetch、事件监听......),把它"记下来",先跳过 - 等同步代码全部跑完,再回头,把前面记下的异步任务一个提出来执行
到底看最开始那段代码,setTimeout就是异步任务。JS看到它,把里面的回调函数调用一个"等候区",继续往下跑console.log('end')。等这些同步的跑完了,再回头把222取出来执行。
所以end在222前面先执行

Promise:更优雅地控制异步流程
光有Event Loop还不够。实际上经常要处理这样的情况:
先请求用户列表,随后获取用户ID,再去请求该用户的详情。
如果全靠调用回调来写,代码会越套越深,难看又难维护。于是 ES6 带来了Promise。
Promise的中文意思是"承诺"------它代表一个未来会有结果的异步任务。
实例化一个Promise需要传一个函数(叫executor),这个函数会立刻执行,里面放运行操作:
js
javascript
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(666); // 成功,把结果传出去
// reject('网络错误'); // 失败,把原因传出去
}, 2000);
});
承诺有清晰状态:
- 待处理:还在等,任务尚未完成
- 已完成 :成功了,
resolve()被调用 - 被拒绝 :失败了,
reject()被调用
状态一旦改变,就不会再变。
获得结果的方式是链式调用:
js
javascript
p.then(data => {
console.log('成功:', data);
})
.catch(err => {
console.log('失败:', err);
})
.finally(() => {
console.log('无论如何都会跑到这里');
});
.then处理成功,.catch处理失败,.finally无论如何都执行------比如关闭加载动画,无论请求成没成功都得关。

用 Promise 造一个睡眠
JS 原生没有sleep()这个东西,但可以用 Promise 自己造一个:
js
javascript
function sleep(t) {
return new Promise(resolve => {
setTimeout(resolve, t);
});
}
sleep(2000).then(() => {
console.log('2秒后才到这里');
});
大概代码做的事情是:创建一个 Promise,2 秒后调用resolve(),于是.then里的内容在 2 秒后执行。
虽然语言没有内置的阻塞,但通过Promise链,同样能控制"等多久再做什么"。
一个常见的坑
在console.log里直接包了fetch:
js
sql
console.log(fetch('https://api.example.com/data'));
结果打印出来的不是数据,而是一个 Promise 对象。
这是因为fetch是异步的,它立即返回的是一个 Promise(状态还是 Pending),数据还没有回来。要获取数据,必须.then等它完成:
js
ini
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.log('请求出错', err));
小结
JS 的这套机制,核心就是三句话:
- 单线程,同步代码老老实实从上到下跑
- 事件循环,异步任务先放一边,等同步跑完再结局处理
- Promise ,用来优雅地描述"异步任务的结果",成功走
.then,失败走.catch