异步编程
JavaScript 是单线程的,意味着它一次只能执行一个任务。如果遇到耗时操作(如网络请求),程序会“阻塞”,导致页面卡顿。为了解决这个问题,JavaScript 使用异步编程,让耗时操作在后台执行,同时继续执行其他代码。
异步的特点:
- 不会阻塞后续代码的执行。
- 耗时操作完成后,通过回调函数、Promise 或 async/await 来处理结果。
也就是实现异步编程有 3 种方法:回调函数、Promise 或 async/await
回调函数容易出现”回调地狱”的情况,所以目前不怎么依赖它来实现异步编程,我们简单介绍即可
回调函数
回调函数本质就是一个函数,只是把它作为参数传递给了另一个函数,并在某个操作完成后被调用。回调函数的核心思想是:
- 延迟执行:在某个条件满足或某个操作完成后,才执行这个函数。
- 异步处理:常用于处理异步操作的结果。
示例:
|
输出结果:
|
Promise
本质上 Promise 是一个函数返回的对象,我们可以在它上面绑定回调函数,这样我们就不需要在一开始把回调函数作为参数传入这个函数了,它表示一个异步操作的最终完成(或失败)及其结果值。你可以把它理解为一个“承诺”:
- 承诺:将来会完成某个操作(成功或失败)
- 结果:操作完成后,会返回一个值(或错误)
Promise 的三种状态
一个 Promise 对象有三种状态:
- Pending(进行中):初始状态,表示操作尚未完成。
- Fulfilled(已成功):表示操作成功完成,并返回结果值。
- Rejected(已失败):表示操作失败,并返回错误原因。
一旦 Promise 的状态从 Pending 变为 Fulfilled 或 Rejected,就不可再改变。
then()
.then()
是 Promise 对象的一个方法,用于处理 Promise 成功完成后的结果。它是 JavaScript 中实现异步编程的核心工具之一,让代码更清晰、更易读
.then()
的作用
- 处理成功的结果:当 Promise 的状态从 Pending 变为 Fulfilled(即成功)时,
.then()
中的回调函数会被调用。 - 链式调用:
.then()
返回一个新的 Promise,可以继续调用下一个.then()
,从而实现多个异步操作的顺序执行。
.then()的语法
|
- **
onFulfilled
**:一个回调函数,当 Promise 成功时调用。它接受一个参数,即 Promise 的成功结果。 - **
onRejected
**(可选):一个回调函数,当 Promise 失败时调用。它接受一个参数,即 Promise 的失败原因。
通常,我们只使用 onFulfilled
,而用 .catch()
来处理失败的情况。
链式调用-示例:
|
输出结果:
|
async/
await
async/await 是 JavaScript 中用于简化异步编程的语法糖。它基于 Promise,但让异步代码的写法更像同步代码,从而更容易理解和维护。
async:用于声明一个异步函数。异步函数会隐式返回一个 Promise
await:用于等待一个 Promise 完成(即 Promise 的状态变为 resolved)。await 只能在 async 函数中使用
promise章节中的代码用async/
await简写如下:
|
💡关于语法糖
不改变语言的功能:语法糖只是提供了一种更简洁的写法,底层实现仍然是原有的语法或机制。提高代码的可读性:语法糖通常会让代码更直观、更易理解。
减少代码量:语法糖可以帮助开发者用更少的代码实现相同的功能。
闭包
闭包是指一个函数能够“记住”并访问它定义时的环境,即使这个函数在定义时的环境之外执行。即 闭包 = 函数 + 引用环境
闭包的核心
函数嵌套:
- 闭包通常发生在函数嵌套的情况下,即一个函数内部定义了另一个函数。
访问外部变量:
- 内部函数可以访问外部函数的变量,即使外部函数已经执行完毕。
“记住”环境:
- 闭包会“记住”它定义时的环境(即外部函数的作用域),即使外部函数已经执行完毕。
示例 1:简单的闭包
|
outer
函数内部有一个变量message
。inner
函数使用了message
。- 即使
outer
函数执行完毕,inner
函数仍然可以访问message
,这就是闭包。
闭包的作用
记住变量:
- 闭包可以让函数“记住”它定义时的变量,即使外部函数已经执行完毕。
创建私有变量:
- 闭包可以用来隐藏变量,避免被外部直接修改。
function createPerson(name) {
let age = 0; // 私有变量
return {
getName: function() {
return name;
},
getAge: function() {
return age;
},
celebrateBirthday: function() {
age++;
}
};
}
const person = createPerson("Alice");
console.log(person.getName()); // 输出: Alice
person.celebrateBirthday();
console.log(person.getAge()); // 输出: 1实现模块化:
- 闭包可以用来隐藏内部实现细节,只暴露必要的功能。
const module = (function() {
const privateVariable = "I am private";
return {
publicMethod: function() {
console.log(privateVariable);
}
};
})();
module.publicMethod(); // 输出: I am private
闭包的注意事项
- 内存泄漏:
- 闭包会一直“记住”外部函数的变量,即使这些变量已经不再需要了。如果闭包用得太多,可能会导致内存占用过高。
- 解决方法:在不需要时手动解除引用。
|
事件循环
JavaScript 是单线程的,这意味着它一次只能执行一个任务。如果所有任务都是同步的,那么一个耗时操作会阻塞整个程序的执行。为了解决这个问题,JavaScript 引入了异步操作和事件循环。
事件循环负责协调异步任务的执行。它的核心任务是:
- 监控调用栈(Call Stack):调用栈是 JavaScript 执行同步代码的地方。
- 管理任务队列(Task Queues):任务队列用于存储待执行的异步任务。
- 在调用栈为空时,将任务队列中的任务推入调用栈执行。
❗我个人用这个用的比较少,后续在nodejs中会继续学习