13 Node.js 小白教程 - 回调函数

13 Node.js 小白教程 - 回调函数

什么是回调函数

在 Node.js 中,回调函数是指在函数执行过程中,通过参数传递给函数的另一个函数。它通常用于处理异步操作的结果。在执行某些操作时,我们不希望阻塞主进程,因此我们使用回调函数来处理这些操作完成后的结果。

为什么使用回调函数

使用回调函数的主要原因是为了实现异步编程。Node.js 是一个单线程模型,使用回调函数可以让程序在执行 I/O 操作时不阻塞,继续处理其他任务。

基本语法

回调函数的基本语法如下:

1
2
3
4
function mainFunction(callback) {
// 执行一些操作
callback(); // 调用回调函数
}

示例

下面的例子演示了如何使用回调函数来处理延迟操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
function fetchData(callback) {
setTimeout(() => {
const data = "数据加载完成";
callback(data);
}, 2000); // 模拟异步操作
}

function processData(data) {
console.log(data);
}

// 调用主函数并传入回调
fetchData(processData);

在这个示例中,fetchData 函数模拟了一个异步操作,使用 setTimeout 函数延迟 2 秒后执行。调用 processData 函数作为回调,用于处理加载完成后的数据。

错误处理

在使用回调函数时,通常会将错误作为第一个参数传递给回调。这意味着如果发生错误,我们可以在回调中处理该错误。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function fetchDataWithError(callback) {
setTimeout(() => {
const error = "加载失败";
callback(error, null); // 第一个参数是错误
}, 2000);
}

function handleData(error, data) {
if (error) {
console.error(error);
return; // 处理错误
}
console.log(data);
}

// 调用主函数并传入回调
fetchDataWithError(handleData);

在这个示例中,fetchDataWithError 函数模拟了一个异步错误情况,调用 handleData 函数作为回调处理错误。

嵌套回调(回调地狱)

使用回调函数时,如果需要多次嵌套,可能会导致代码可读性差,这种现象被称为“回调地狱”。我们可以通过合理的结构来减少嵌套层级。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function firstTask(callback) {
setTimeout(() => {
console.log("第一项任务完成");
callback();
}, 1000);
}

function secondTask(callback) {
setTimeout(() => {
console.log("第二项任务完成");
callback();
}, 1000);
}

function thirdTask(callback) {
setTimeout(() => {
console.log("第三项任务完成");
callback();
}, 1000);
}

// 回调地狱示例
firstTask(() => {
secondTask(() => {
thirdTask(() => {
console.log("所有任务完成");
});
});
});

在这个示例中,我们可以看到中间的多个嵌套。虽然它能工作,但可读性差,维护起来也不方便。

解决回调地狱

为了解决回调地狱的问题,我们可以使用 Promiseasync/await 来简化异步代码结构。

使用 Promise

下面是如何将回调函数改写为使用 Promise 的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function firstTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("第一项任务完成");
resolve();
}, 1000);
});
}

function secondTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("第二项任务完成");
resolve();
}, 1000);
});
}

function thirdTask() {
return new Promise((resolve) => {
setTimeout(() => {
console.log("第三项任务完成");
resolve();
}, 1000);
});
}

// 使用 Promise 处理顺序
firstTask()
.then(secondTask)
.then(thirdTask)
.then(() => {
console.log("所有任务完成");
});

使用 async/await

1
2
3
4
5
6
7
8
9
async function executeTasks() {
await firstTask();
await secondTask();
await thirdTask();
console.log("所有任务完成");
}

// 调用 async 函数
executeTasks();

通过上述方法,我们可以减少嵌套,提升代码的可读性和维护性。

总结

回调函数是 Node.js 中进行异步编程的重要组成部分。尽管回调函数在处理异步操作时非常有效,但我们也要注意避免回调地狱的问题。可以使用 Promiseasync/await 来提高代码的可读性。通过这些示例,你应该能够理解回调函数的基本用法及其在 Node.js 中的重要性。

14 Promise 使用指南

14 Promise 使用指南

什么是 Promise

Promise 是一种用于处理异步操作的对象。它代表了一个可能在未来某个时间点完成的操作,可能返回一个值或抛出一个错误。Promise 有三种状态:

  • pending(进行中)
  • fulfilled(已实现)
  • rejected(已拒绝)

Promise 的基本使用

创建一个 Promise 非常简单。使用 new Promise 可以创建一个新的 Promise 实例,并传入一个执行器函数,接收两个参数:resolvereject。当异步操作成功完成时,调用 resolve;如果失败,则调用 reject

1
2
3
4
5
6
7
8
const myPromise = new Promise((resolve, reject) => {
const success = true; // 模拟成功情况
if (success) {
resolve("操作成功");
} else {
reject("操作失败");
}
});

处理 Promise

通过 thencatch 方法来处理 Promise 的结果。

1
2
3
4
5
6
7
myPromise
.then((result) => {
console.log(result); // 输出: 操作成功
})
.catch((error) => {
console.log(error);
});

链式调用

Promise 允许链式调用,即在一个 then 方法的返回值是另一个 Promise,你可以继续使用 then

1
2
3
4
5
6
7
8
9
10
11
myPromise
.then((result) => {
console.log(result); // 输出: 操作成功
return new Promise((resolve) => resolve("下一个操作成功"));
})
.then((nextResult) => {
console.log(nextResult); // 输出: 下一个操作成功
})
.catch((error) => {
console.log(error);
});

Promise.all 和 Promise.race

Promise.all

Promise.all 方法用于处理多个 Promise,当所有 Promise 都成功时返回一个 Promise,如果有任何一个 Promise 失败,则返回的 Promise 也会失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.reject("失败");

Promise.all([promise1, promise2])
.then((values) => {
console.log(values); // 输出: [1, 2]
})
.catch((error) => {
console.log(error);
});

Promise.all([promise1, promise3])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.log(error); // 输出: 失败
});

Promise.race

Promise.race 方法同样用于处理多个 Promise,但是它返回第一个完成的 Promise 的结果,无论是成功还是失败。

1
2
3
4
5
6
7
const promiseA = new Promise((resolve) => setTimeout(() => resolve("A"), 1000));
const promiseB = new Promise((resolve) => setTimeout(() => resolve("B"), 500));

Promise.race([promiseA, promiseB])
.then((result) => {
console.log(result); // 输出: B
});

Async/Await 语法

asyncawait 是对 Promise 的进一步语法糖,使得异步代码看起来更像同步代码,便于阅读。

1
2
3
4
5
6
7
8
9
10
const asyncFunction = async () => {
try {
const result = await myPromise;
console.log(result); // 输出: 操作成功
} catch (error) {
console.log(error);
}
};

asyncFunction();

async 函数返回值

任何 async 函数都会返回一个 Promise。即使是一个普通的值,async 也会将其包装成 Promise

1
2
3
4
5
6
7
const anotherAsyncFunction = async () => {
return "这是一个值"; // 实际上会得到 Promise.resolve("这是一个值")
};

anotherAsyncFunction().then((value) => {
console.log(value); // 输出: 这是一个值
});

小结

使用 Promise 处理异步操作可以使得代码更加清晰和可维护。通过 thencatchfinally 方法,您可以轻松处理成功与失败的结果。同时,结合 async/await 语法,您可以使异步代码更易读。掌握 Promise 是成为 JavaScript 开发者的重要一步。

15 async/await 在 Node.js 中的使用

15 async/await 在 Node.js 中的使用

在 Node.js 中,async/await 是处理异步代码的一种强大方式。它使得异步代码看起来像同步代码,便于阅读和维护。

基础知识

asyncawait 是 ES2017(ES8)引入的特性。你可以用 async 声明一个函数,该函数会返回一个 Promise。在这个 async 函数内部,你可以使用 await 关键字等待一个 Promise 的结果。

async 函数

async 函数的定义很简单,只需在函数前加上 async 关键字:

1
2
3
async function fetchData() {
return "数据准备好了!";
}

调用这个函数会返回一个 Promise

1
fetchData().then(data => console.log(data)); // 输出: 数据准备好了!

await 关键字

await 只能在 async 函数中使用。它会使 async 函数暂停执行,直到 Promise 被解决(resolved)或拒绝(rejected)。

1
2
3
4
5
6
7
8
9
10
async function fetchData() {
const data = await new Promise((resolve) => {
setTimeout(() => {
resolve("数据准备好了!");
}, 1000);
});
console.log(data);
}

fetchData(); // 1秒后输出: 数据准备好了!

使用场景示例

1. 简化异步请求

使用 async/await 可以让 HTTP 请求的处理过程变得更简单明了。例如,使用 axios 进行异步数据请求:

1
2
3
4
5
6
7
8
9
10
11
12
const axios = require('axios');

async function getUserData(userId) {
try {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
console.log(response.data);
} catch (error) {
console.error("获取用户数据时出错:", error);
}
}

getUserData(1); // 输出用户数据

2. 顺序执行多个异步操作

当你需要按顺序执行多个异步操作时,使用 async/await 会让代码逻辑更加清晰。

1
2
3
4
5
6
7
8
9
async function processMultipleRequests() {
const data1 = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
console.log(data1.data);

const data2 = await axios.get('https://jsonplaceholder.typicode.com/posts/2');
console.log(data2.data);
}

processMultipleRequests();

在这个示例中,data1data2 的请求是顺序执行的。

3. 并行处理多个异步操作

如果你希望并行触发多个异步请求,可以结合 Promise.all()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function fetchAllData() {
try {
const [users, posts] = await Promise.all([
axios.get('https://jsonplaceholder.typicode.com/users'),
axios.get('https://jsonplaceholder.typicode.com/posts')
]);
console.log(users.data);
console.log(posts.data);
} catch (error) {
console.error("获取数据时出错:", error);
}
}

fetchAllData();

在这个示例中,usersposts 的请求将同时进行,从而提高了效率。

错误处理

在使用 async/await 时,你可以使用 try/catch 块来捕获和处理异常。

1
2
3
4
5
6
7
8
9
10
async function riskyFunction() {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/invalid');
console.log(response.data);
} catch (error) {
console.error("请求失败:", error);
}
}

riskyFunction(); // 输出: 请求失败: [错误信息]

总结

async/await 是 Node.js 中处理异步编程的强大工具,使得代码更加简洁和易于理解。通过结合 Promiseasync/await,你能够高效地管理复杂的异步流程,并且能够方便地进行错误处理。掌握这一特性,将极大地提升你的 Node.js 编程体验。