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); }); });结束