25 JavaScript 回调函数入门

25 JavaScript 回调函数入门

在 JavaScript 中,回调函数是一个函数,它作为参数传递给另一个函数,并在适当的时候被调用。理解回调函数可以极大地提高你对 JavaScript 异步编程和事件处理的理解。

什么是回调函数?

简单来说,回调函数就是一个可以“调用”的函数。通常情况下,我们会将其作为参数传递,待特定条件满足后再执行。这种机制使得代码更加灵活和可扩展。

示例:

1
2
3
4
5
6
7
8
9
10
11
function greet(name) {
console.log(`Hello, ${name}!`);
}

function processUserInput(callback) {
const name = prompt("Please enter your name:");
callback(name);
}

// 调用 processUserInput,并传入 greet 函数作为回调
processUserInput(greet);

在上面的示例中,greet 是一个回调函数,它被传递给 processUserInput,并在用户输入名字后被调用。

回调函数的应用场景

回调函数广泛应用于事件处理、异步操作等场景。比如,在 AJAX 请求、定时器和 DOM 事件中常常会使用回调函数。

AJAX 请求示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function fetchData(url, callback) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
callback(null, xhr.responseText);
} else {
callback(`Error: ${xhr.status}`);
}
};
xhr.send();
}

fetchData('https://jsonplaceholder.typicode.com/posts', function(err, data) {
if (err) {
console.error(err);
return;
}
console.log('Data received:', data);
});

在这个例子中,fetchData 函数通过 XMLHttpRequest 消耗一个 URL。请求完成后,callback 被调用,这使得数据处理的逻辑与数据获取的逻辑解耦。

匿名回调函数

你可以直接在调用时定义一个匿名函数,而不需要事先定义。这样可以编写更加简洁的代码。

示例:

1
2
3
setTimeout(function() {
console.log("This message is delayed by 2 seconds.");
}, 2000);

在上述示例中,setTimeout 的第一个参数是一个匿名回调函数,它将在 2 秒后执行。

回调地狱

虽然回调函数十分强大,但当多个回调嵌套时,代码会变得难以阅读和维护,这种现象称为“回调地狱”。

示例:

1
2
3
4
5
6
7
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log("Got the final result: " + finalResult);
});
});
});

为了改善这种情况,我们可以考虑使用 Promiseasync/await 语法来提高代码的可读性。

小结

回调函数是 JavaScript 中非常重要的一个概念。它们使我们能够构建灵活的应用程序逻辑,并处理异步操作。掌握回调函数的使用,无疑将为你理解更复杂的概念铺平道路。

通过上述示例,希望你对回调函数有了更深刻的理解,并能在实际项目中灵活应用。

26 JavaScript 中的 Promise

26 JavaScript 中的 Promise

什么是 Promise?

Promise 是 JavaScript 中用于处理异步操作的一种机制。它代表一个尚未完成但预计将来会完成的操作,并提供了一种方法来处理最终的结果(成功或失败)。

Promise 的状态

一个 Promise 可以处于以下三种状态之一:

  • Pending(待定):初始状态,表示异步操作尚未完成。
  • Fulfilled(已兑现):表示异步操作成功完成。
  • Rejected(已拒绝):表示异步操作失败。
1
2
3
4
5
6
7
8
9
10
11
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const success = true; // 根据需求设置成功或失败
if (success) {
resolve("操作成功");
} else {
reject("操作失败");
}
}, 1000);
});

创建和使用 Promise

基本用法

我们可以使用 then()catch() 方法来处理 Promise 的结果。

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

示例:加载数据

下面是一个关于如何使用 Promise 加载数据的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { name: "JavaScript", type: "Programming Language" };
resolve(data);
}, 2000);
});
}

fetchData()
.then(data => {
console.log("数据加载成功:", data);
})
.catch(error => {
console.error("数据加载失败:", error);
});

Promise 链

你可以将多个 Promise 链接在一起,以实现多个异步操作的顺序执行。

1
2
3
4
5
6
7
8
9
10
11
fetchData()
.then(data => {
console.log("第一步:", data);
return fetchData(); // 返回下一个 Promise
})
.then(data => {
console.log("第二步:", data);
})
.catch(error => {
console.error("错误发生:", error);
});

Promise.all

当你需要并行处理多个 Promise 时,可以使用 Promise.all

1
2
3
4
5
6
7
8
9
10
const promise1 = fetchData();
const promise2 = fetchData();

Promise.all([promise1, promise2])
.then(results => {
console.log("所有数据加载成功:", results);
})
.catch(error => {
console.error("至少有一个 Promise 失败:", error);
});

Promise.race

Promise.race 方法会返回第一个完成的 Promise

1
2
3
4
5
6
7
8
9
10
11
const promise1 = new Promise((resolve) =>
setTimeout(() => resolve("第一个 Promise 完成"), 1000)
);
const promise2 = new Promise((resolve) =>
setTimeout(() => resolve("第二个 Promise 完成"), 500)
);

Promise.race([promise1, promise2])
.then(result => {
console.log("Race Result:", result); // 输出 "第二个 Promise 完成"
});

总结

Promise 是处理异步操作的强大工具。通过理解和掌握 Promise,你可以更轻松地编写高效、可维护的JavaScript代码。当与 then()catch()Promise.all()Promise.race() 等方法结合使用时,它们可以有效地管理多个异步操作。

27 理解 async/await

27 理解 async/await

在现代 JavaScript 中,async/await 是处理异步操作的一种简洁方式。它建立在 Promise 的基础上,使得异步代码的写法更加接近同步代码,从而提高代码的可读性和维护性。

基础概念

async 函数

使用 async 关键字定义的函数称为 async 函数。async 函数总是返回一个 Promise 对象。如果函数内部返回一个值,则返回的 Promise 会自动解析为该值;如果返回的是一个错误,则返回的 Promise 会被拒绝。

示例:

1
2
3
4
5
6
7
async function example() {
return "Hello, World!";
}

example().then(result => {
console.log(result); // 输出 "Hello, World!"
});

在上述代码中,example 函数返回一个 Promise,我们可以使用 .then() 方法来处理结果。

await 操作符

await 关键字只能在 async 函数内部使用。它用于等待一个 Promise 的解决,并返回解决后的值。如果 Promise 被拒绝,await 将抛出错误。

示例:

1
2
3
4
5
6
7
8
9
10
11
async function fetchData() {
let response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
let data = await response.json();
return data;
}

fetchData().then(data => {
console.log(data); // 输出获取到的 JSON 数据
}).catch(error => {
console.error('Error:', error);
});

在这个例子中,fetchData 函数首先使用 await 等待 fetch 请求的结果,然后再调用 response.json()。这使得代码更加易读,特别是处理多个异步操作时。

错误处理

在使用 async/await 时,可以使用 try/catch 语句来处理错误。这使得错误处理更加清晰。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async function fetchPosts() {
try {
let response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Network response was not ok');
}
let posts = await response.json();
console.log(posts);
} catch (error) {
console.error('Fetch error:', error);
}
}

fetchPosts();

在这个例子中,使用 try/catch 来捕获在网络请求或数据解析过程中可能发生的错误。

多个异步操作

如果你需要并行处理多个异步操作,可以结合 Promise.all()await 使用。Promise.all() 方法接受一个数组的 Promise 对象,并返回一个新的 Promise,只有当所有 Promise 都解决的时,才解决。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function fetchMultiplePosts() {
try {
const urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3'
];

const promises = urls.map(url => fetch(url));
const responses = await Promise.all(promises);

const data = await Promise.all(responses.map(response => response.json()));
console.log(data);
} catch (error) {
console.error('Error fetching multiple posts:', error);
}
}

fetchMultiplePosts();

在这个例子中,我们并行请求多个 URL,并在所有请求完成后解析其结果。这种方式比逐个请求要高效得多。

总结

async/await 提供了一种优雅的方式来处理异步操作。通过使用 async 关键字定义函数,结合 await 关键字等待 Promise,我们可以编写出更具可读性的代码。异常处理也变得更加清晰,使用 try/catch 语法可以有效捕获和处理错误。在处理多个异步操作时,结合 Promise.all() 可以大大提高性能。