闽公网安备 35020302035485号
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error fetching data:', error);
// 这里可以根据需求处理错误,比如向用户显示错误信息
}
}
上面的代码中,我们定义了一个名为fetchData的异步函数,用于从API获取数据。通过使用try...catch块,我们可以优雅地捕获和处理可能发生的错误,而不是让错误在后台悄悄发生。async function loadUserAndPosts(userId) {
const user = await fetch(`https://api.example.com/users/${userId}`);
const userData = await user.json();
const posts = await fetch(`https://api.example.com/users/${userId}/posts`);
const postData = await posts.json();
return { user: userData, posts: postData };
}
在这个例子中,我们定义了一个名为loadUserAndPosts的异步函数,接收一个用户ID作为参数。该函数依次执行以下操作:async function fetchAllData() {
const [user, posts, comments] = await Promise.all([
fetch('https://api.example.com/users/1'),
fetch('https://api.example.com/posts'),
fetch('https://api.example.com/comments')
]);
const userData = await user.json();
const postData = await posts.json();
const commentsData = await comments.json();
return { user: userData, posts: postData, comments: commentsData };
}
在这个例子中,我们定义了一个名为fetchAllData的异步函数。该函数使用Promise.all并行执行三个fetch请求,分别获取用户信息、帖子和评论。具体步骤如下:.将解析后的数据组合成一个对象,并返回这个对象。
async function fetchDataWithTimeout(url, timeout = 5000) {
const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Request timed out')), timeout);
});
return Promise.race([fetch(url), timeoutPromise]);
}
这个例子中,我们定义了一个名为fetchDataWithTimeout的异步函数,用于在指定时间内发起网络请求。如果超过设定的超时时间(默认5000毫秒),请求将自动失败。具体步骤如下:const timeoutPromise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Request timed out')), timeout);
});
这个Promise在超时时间到达后会自动拒绝,触发超时错误。// 堆代码 duidaima.com return Promise.race([fetch(url), timeoutPromise]);Promise.race会同时执行fetch请求和超时Promise,哪个Promise先解决,fetchDataWithTimeout函数就会返回哪个Promise的结果。这样,如果fetch请求在超时时间内完成,就返回其结果;如果超时,则返回超时错误。通过这种方式,我们可以避免网络请求长时间挂起,提升应用的可靠性和用户体验。特别是在网络状况不佳或服务端响应慢的情况下,设置超时可以确保应用不会因为等待请求而卡死。
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request cancelled');
} else {
console.error('Error fetching data:', error);
}
});
// 稍后,如果需要取消请求:
controller.abort();
在这个例子中,我们使用AbortController来实现对请求的取消。具体步骤如下:const controller = new AbortController();AbortController是一个可以用来控制fetch请求的接口。
const signal = controller.signal;signal是一个AbortSignal对象,它可以用来与fetch请求关联。
fetch('https://api.example.com/data', { signal })
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
if (error.name === 'AbortError') {
console.log('Request cancelled');
} else {
console.error('Error fetching data:', error);
}
});
在发送fetch请求时,将signal传递进去。这使得该请求可以被AbortController控制。controller.abort();调用controller.abort()方法可以取消关联的请求。如果请求被取消,fetch会抛出一个AbortError,可以在catch块中捕获并处理。通过这种方式,我们可以灵活地控制网络请求,避免不必要的资源消耗和潜在的性能问题。在用户快速切换页面或重复触发请求时,这种方法尤为有效。
const observable = new RxJS.Observable(subscriber => {
// 模拟数据流的异步获取
setTimeout(() => subscriber.next(1), 1000);
setTimeout(() => subscriber.next(2), 2000);
setTimeout(() => subscriber.complete(), 3000);
});
const subscription = observable.subscribe(
value => console.log('接收到的值:', value),
error => console.error('错误:', error),
() => console.log('数据流完成')
);
// 稍后,如果需要取消订阅:
subscription.unsubscribe();
在这个例子中,我们使用RxJS创建了一个Observable,并模拟了异步数据流。具体步骤如下:const observable = new RxJS.Observable(subscriber => {
setTimeout(() => subscriber.next(1), 1000);
setTimeout(() => subscriber.next(2), 2000);
setTimeout(() => subscriber.complete(), 3000);
});
我们通过创建一个新的Observable来模拟数据的异步获取。这里用setTimeout函数在指定时间后发送数据。const subscription = observable.subscribe(
value => console.log('接收到的值:', value),
error => console.error('错误:', error),
() => console.log('数据流完成')
);
使用subscribe方法订阅这个Observable。订阅时可以指定三个回调函数:一个用于处理接收到的数据,一个用于处理错误,另一个用于处理数据流完成的情况。subscription.unsubscribe();当你不再需要接收数据时,可以通过调用unsubscribe方法取消订阅。这有助于避免内存泄漏和不必要的资源消耗。使用RxJS和Observables,你可以轻松处理复杂的异步数据流。例如,结合多个数据源、处理连续的数据更新、以及优雅地处理错误和完成状态。RxJS的强大之处在于它提供了丰富的操作符,可以对数据流进行各种转换和组合,让你的代码更具表达力和可维护性。
async function* fetchPosts() {
let page = 1;
while (true) {
const response = await fetch(`https://api.example.com/posts?page=${page}`);
const data = await response.json();
if (data.length === 0) {
break;
}
for (const post of data) {
yield post;
}
page++;
}
}
(async () => {
for await (const post of fetchPosts()) {
console.log('Post:', post);
}
})();
在这个例子中,我们创建了一个名为fetchPosts的异步生成器函数,用于逐页获取帖子数据。每次从API获取新的一页数据,如果没有数据了,就结束循环。如果有数据,就逐个yield返回每条帖子。let timeoutId;
const debouncedSearch = (searchTerm) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
console.log('Search for:', searchTerm);
}, 500); // 延迟500毫秒
};
在这个例子中,每次用户输入时,都会清除上一次的计时器,并重新设置一个新的计时器。当用户停止输入超过500毫秒后,才会执行搜索操作。let isFetching = false;
const throttledFetchData = async () => {
if (isFetching) return;
isFetching = true;
const data = await fetch('https://api.example.com/data');
// 处理数据
isFetching = false;
};
在这个例子中,通过isFetching标志位来控制请求频率,防止在数据请求完成之前再次触发请求。document.getElementById('searchInput').addEventListener('input', (event) => {
debouncedSearch(event.target.value);
});
节流(Throttling)实战:在窗口滚动事件中应用节流技术,避免频繁执行回调函数,提升性能。window.addEventListener('scroll', throttledFetchData);
通过使用防抖和节流技术,你可以有效减少不必要的操作,提升应用的性能和用户体验。这两种技术简单而高效,适用于各种需要优化高频率事件处理的场景。fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error fetching data:', error));
重试逻辑与指数退避async function fetchDataWithRetry(url, retries = 3, delay = 1000) {
for (let i = 0; i < retries; i++) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (i < retries - 1) {
await new Promise(res => setTimeout(res, delay * Math.pow(2, i)));
} else {
throw error;
}
}
}
}
fetchDataWithRetry('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error('Failed to fetch data after retries:', error));
断路器模式class CircuitBreaker {
constructor(fn, failureThreshold = 5, cooldownPeriod = 10000) {
this.fn = fn;
this.failureThreshold = failureThreshold;
this.cooldownPeriod = cooldownPeriod;
this.failures = 0;
this.lastFailureTime = null;
}
async execute(...args) {
if (this.failures >= this.failureThreshold) {
if (Date.now() - this.lastFailureTime > this.cooldownPeriod) {
this.failures = 0;
} else {
throw new Error('Circuit breaker is open');
}
}
try {
const result = await this.fn(...args);
this.failures = 0;
return result;
} catch (error) {
this.failures += 1;
this.lastFailureTime = Date.now();
throw error;
}
}
}
// 使用示例
const fetchWithCircuitBreaker = new CircuitBreaker(fetchDataWithRetry);
fetchWithCircuitBreaker.execute('https://api.example.com/data')
.then(data => console.log(data))
.catch(error => console.error('Failed with circuit breaker:', error));
10、 测试异步代码test('fetches data successfully', async () => {
const data = { name: 'John' };
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve(data)
})
);
const result = await fetchDataWithRetry('https://api.example.com/data');
expect(result).toEqual(data);
})
使用 Mocha 进行异步测试const assert = require('assert');
const sinon = require('sinon');
// 这里无需引入 fetch,因为我们使用 sinon.stub 进行模拟
// 假设 fetchDataWithRetry 函数已正确引入
const fetchDataWithRetry = require('./path/to/fetchDataWithRetry'); // 修改为实际路径
describe('fetchDataWithRetry', function() {
let fetchStub;
beforeEach(function() {
// 确保在 Node.js 环境中存在全局 fetch
if (!global.fetch) {
global.fetch = () => {};
}
fetchStub = sinon.stub(global, 'fetch');
});
afterEach(function() {
fetchStub.restore();
});
it('should fetch data successfully', async function() {
const data = { name: 'John' };
fetchStub.resolves({
ok: true,
json: () => Promise.resolve(data)
});
const result = await fetchDataWithRetry('https://api.example.com/data');
assert.deepStrictEqual(result, data);
});
});
结束