Ajax、Axios和Fetch的用法和区别,撤回请求,防抖节流,针对处理高频率请求的场景如何处理?

596 阅读6分钟

在现代JavaScript开发中,用于进行HTTP请求的三种主要方式是Ajax、Axios和Fetch。这三种方式各有优缺点,并且适用于不同的场景。在合适的业务场景下使用,以下是它们的区别和使用举例。并且实现撤回或取消请求,减少不必要的重复请求,在同时有高频率大量请求时,放入请求队列,以及前端的防抖或者节流,提高前端性能优化。

1. Ajax

Ajax(Asynchronous JavaScript and XML)是一种使用JavaScript和XML进行异步网页更新的技术。尽管其名称中包含XML,但它可以处理多种数据格式,包括JSON、HTML和纯文本。传统上,Ajax使用的是XMLHttpRequest对象。

Ajax 示例

<!DOCTYPE html>
<html>
<head>
    <title>Ajax Example</title>
</head>
<body>
    <button id="loadData">Load Data</button>
    <div id="result"></div>

    <script>
        document.getElementById('loadData').addEventListener('click', function() {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    document.getElementById('result').innerHTML = xhr.responseText;
                }
            };
            xhr.send();
        });
    </script>
</body>
</html>

2. Axios

Axios是一个基于Promise的HTTP库,可以用于浏览器和Node.js。它具有简单易用的API,支持拦截请求和响应、取消请求、自动转换JSON数据等功能。

安装 Axios

在使用Axios之前,需要安装它:

npm install axios

Axios 示例

<!DOCTYPE html>
<html>
<head>
    <title>Axios Example</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <button id="loadData">Load Data</button>
    <div id="result"></div>

    <script>
        document.getElementById('loadData').addEventListener('click', function() {
            axios.get('https://jsonplaceholder.typicode.com/posts/1')
                .then(function(response) {
                    document.getElementById('result').innerHTML = JSON.stringify(response.data, null, 2);
                })
                .catch(function(error) {
                    console.error('Error:', error);
                });
        });
    </script>
</body>
</html>

3. Fetch

Fetch API是现代浏览器中用来替代XMLHttpRequest的,提供了一个更强大和灵活的方式来发起网络请求。它基于Promise,语法更加简洁。

Fetch 示例

<!DOCTYPE html>
<html>
<head>
    <title>Fetch Example</title>
</head>
<body>
    <button id="loadData">Load Data</button>
    <div id="result"></div>

    <script>
        document.getElementById('loadData').addEventListener('click', function() {
            fetch('https://jsonplaceholder.typicode.com/posts/1')
                .then(response => response.json())
                .then(data => {
                    document.getElementById('result').innerHTML = JSON.stringify(data, null, 2);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        });
    </script>
</body>
</html>

区别与比较

  1. 使用简便性

    • Ajax:使用XMLHttpRequest对象,需要处理各种状态和事件,代码较为冗长。
    • Axios:基于Promise,API设计更简洁,使用更方便,支持更多功能。
    • Fetch:原生Promise支持,语法简洁,但需要处理一些低级错误(例如网络错误不会被捕捉到,需要手动处理response.ok)。
  2. 浏览器支持

    • Ajax:所有现代浏览器都支持。
    • Axios:需要引入外部库,但支持所有现代浏览器。
    • Fetch:所有现代浏览器(Edge开始支持),但对于老版本浏览器(如IE)需要使用polyfill。
  3. 功能特性

    • Ajax:功能较为基础,需要手动处理各种请求和响应。
    • Axios:支持请求和响应拦截器、自动转换JSON数据、取消请求等高级功能。
    • Fetch:提供基本功能,响应处理需要手动转换(例如JSON),且不支持progress事件和取消请求。

撤回或者取消请求,实现请求的防抖节流

在前端开发中,撤回或取消HTTP请求,防抖和节流都是优化网络请求和提升用户体验的重要技术。以下是详细的解释和实现方法。

1. 撤回或取消HTTP请求

Axios

Axios 提供了取消请求的功能,通过使用 CancelToken

取消请求示例

<!DOCTYPE html>
<html>
<head>
    <title>Axios Cancel Request Example</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <button id="startRequest">Start Request</button>
    <button id="cancelRequest">Cancel Request</button>
    <div id="result"></div>

    <script>
        let cancelTokenSource;

        document.getElementById('startRequest').addEventListener('click', function() {
            cancelTokenSource = axios.CancelToken.source();

            axios.get('https://jsonplaceholder.typicode.com/posts/1', {
                cancelToken: cancelTokenSource.token
            })
            .then(function(response) {
                document.getElementById('result').innerHTML = JSON.stringify(response.data, null, 2);
            })
            .catch(function(error) {
                if (axios.isCancel(error)) {
                    console.log('Request canceled', error.message);
                } else {
                    console.error('Error:', error);
                }
            });
        });

        document.getElementById('cancelRequest').addEventListener('click', function() {
            if (cancelTokenSource) {
                cancelTokenSource.cancel('Request canceled by the user.');
            }
        });
    </script>
</body>
</html>

Fetch

Fetch API 原生不支持取消请求,但可以通过 AbortController 来实现。

取消请求示例

<!DOCTYPE html>
<html>
<head>
    <title>Fetch Cancel Request Example</title>
</head>
<body>
    <button id="startRequest">Start Request</button>
    <button id="cancelRequest">Cancel Request</button>
    <div id="result"></div>

    <script>
        let controller;

        document.getElementById('startRequest').addEventListener('click', function() {
            controller = new AbortController();
            const signal = controller.signal;

            fetch('https://jsonplaceholder.typicode.com/posts/1', { signal })
                .then(response => response.json())
                .then(data => {
                    document.getElementById('result').innerHTML = JSON.stringify(data, null, 2);
                })
                .catch(error => {
                    if (error.name === 'AbortError') {
                        console.log('Request canceled');
                    } else {
                        console.error('Error:', error);
                    }
                });
        });

        document.getElementById('cancelRequest').addEventListener('click', function() {
            if (controller) {
                controller.abort();
            }
        });
    </script>
</body>
</html>

2. 请求的防抖和节流

防抖(Debounce)和节流(Throttle)技术用于控制函数执行频率,特别适用于网络请求和用户输入处理。

防抖 (Debounce)

防抖的实现原理是当连续触发事件时,只有在最后一次触发的指定时间内没有再次触发,才执行函数。

防抖函数示例

function debounce(func, wait) {
    let timeout;
    return function(...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
            func.apply(this, args);
        }, wait);
    };
}

防抖请求示例

<!DOCTYPE html>
<html>
<head>
    <title>Debounce Example</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <input type="text" id="searchInput" placeholder="Type to search..." />
    <div id="result"></div>

    <script>
        const searchInput = document.getElementById('searchInput');
        const result = document.getElementById('result');

        const debounceSearch = debounce(function(query) {
            axios.get(`https://jsonplaceholder.typicode.com/posts?q=${query}`)
                .then(response => {
                    result.innerHTML = JSON.stringify(response.data, null, 2);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }, 500);

        searchInput.addEventListener('input', function() {
            const query = searchInput.value;
            if (query) {
                debounceSearch(query);
            } else {
                result.innerHTML = '';
            }
        });

        function debounce(func, wait) {
            let timeout;
            return function(...args) {
                clearTimeout(timeout);
                timeout = setTimeout(() => {
                    func.apply(this, args);
                }, wait);
            };
        }
    </script>
</body>
</html>

节流 (Throttle)

节流的实现原理是当连续触发事件时,在指定时间内只执行一次函数。

节流函数示例

function throttle(func, limit) {
    let inThrottle;
    return function(...args) {
        if (!inThrottle) {
            func.apply(this, args);
            inThrottle = true;
            setTimeout(() => inThrottle = false, limit);
        }
    };
}

节流请求示例

<!DOCTYPE html>
<html>
<head>
    <title>Throttle Example</title>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
    <input type="text" id="searchInput" placeholder="Type to search..." />
    <div id="result"></div>

    <script>
        const searchInput = document.getElementById('searchInput');
        const result = document.getElementById('result');

        const throttleSearch = throttle(function(query) {
            axios.get(`https://jsonplaceholder.typicode.com/posts?q=${query}`)
                .then(response => {
                    result.innerHTML = JSON.stringify(response.data, null, 2);
                })
                .catch(error => {
                    console.error('Error:', error);
                });
        }, 1000);

        searchInput.addEventListener('input', function() {
            const query = searchInput.value;
            if (query) {
                throttleSearch(query);
            } else {
                result.innerHTML = '';
            }
        });

        function throttle(func, limit) {
            let inThrottle;
            return function(...args) {
                if (!inThrottle) {
                    func.apply(this, args);
                    inThrottle = true;
                    setTimeout(() => inThrottle = false, limit);
                }
            };
        }
    </script>
</body>
</html>

前端如何处理高并发请求

在前端开发中,控制同时发出的请求数量并将剩余请求放入请求队列是一种常见的优化策略,特别是在处理高频率或大量请求的场景下。这种策略可以防止浏览器过载,提高应用的性能和用户体验。

一个合理的并发请求数量通常取决于具体的应用场景和目标用户的网络环境。一般来说,保持同时处理 4 到 6 个请求是一个比较合适的范围。这是因为:

  1. 浏览器限制:大多数浏览器对于同一域名的并发请求有一定的限制,通常为 6 个左右。如果超过这个限制,剩余的请求会被阻塞,直到前面的请求完成。
  2. 服务器负载:过多的并发请求可能会对服务器造成压力,导致响应时间变慢,甚至出现错误。
  3. 用户体验:适当的并发请求数量可以确保页面加载速度和交互响应时间处于合理水平,避免因过多请求导致页面卡顿或加载缓慢。

下面是实现并发请求控制和请求队列的示例代码:

使用 Promise 和 队列 实现并发请求控制

以下示例实现了一个简单的请求队列,只允许同时发出最多 maxConcurrent 个请求。

class RequestQueue {
    constructor(maxConcurrent) {
        this.maxConcurrent = maxConcurrent;
        this.queue = [];
        this.activeCount = 0;
    }

    enqueue(requestFunction) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                requestFunction,
                resolve,
                reject
            });
            this.processQueue();
        });
    }

    processQueue() {
        if (this.activeCount < this.maxConcurrent && this.queue.length > 0) {
            const { requestFunction, resolve, reject } = this.queue.shift();
            this.activeCount++;
            requestFunction()
                .then(result => {
                    resolve(result);
                    this.activeCount--;
                    this.processQueue();
                })
                .catch(error => {
                    reject(error);
                    this.activeCount--;
                    this.processQueue();
                });
        }
    }
}

// 示例使用
const requestQueue = new RequestQueue(5);

function createRequest(url) {
    return () => fetch(url).then(response => response.json());
}

const urls = [
    'https://jsonplaceholder.typicode.com/posts/1',
    'https://jsonplaceholder.typicode.com/posts/2',
    'https://jsonplaceholder.typicode.com/posts/3',
    'https://jsonplaceholder.typicode.com/posts/4',
    'https://jsonplaceholder.typicode.com/posts/5',
    'https://jsonplaceholder.typicode.com/posts/6',
    'https://jsonplaceholder.typicode.com/posts/7',
    'https://jsonplaceholder.typicode.com/posts/8'
];

urls.forEach(url => {
    requestQueue.enqueue(createRequest(url))
        .then(data => {
            console.log('Request succeeded:', data);
        })
        .catch(error => {
            console.error('Request failed:', error);
        });
});

解释

  1. RequestQueue 类:管理请求队列并控制并发数量。maxConcurrent 参数指定最大并发请求数量。
  2. enqueue 方法:将请求函数加入队列,并返回一个 Promise。
  3. processQueue 方法:处理队列中的请求,如果当前活跃的请求数量少于最大并发数,则从队列中取出一个请求并执行。
  4. createRequest 函数:创建一个返回 Promise 的请求函数。
  5. 示例使用:创建 RequestQueue 实例并将请求函数加入队列。

调整策略

根据具体需求和测试结果,调整 maxConcurrent 的值,以找到最佳的并发请求数量。对于不同的网络环境和服务器性能,可能需要不同的并发请求数。建议通过实际测试来确定最适合的设置。

通过这种方式,你可以有效地控制并发请求数量,提升应用性能,并确保用户体验的一致性。

总结

性能优化就好像海绵里的水,挤挤总会有的,不放过每一步,细节决定成功!!!让我们一起赢在细节。