javascript - 累积 id 以发出单个 ajax 请求
问题描述
我有多个地方需要发出 ajax 请求来获取与某些 id 对应的项目。但是,我只想通过累积这些 id 并消除发出 ajax 请求的实际方法来发出一个请求……到目前为止,我已经想出了这段代码,但它只是感觉丑陋/不可重用。
有没有更简单/推荐的方法来实现类似的结果而无需像我在这里所做的那样共享resolve
/变量?promise
这是一个小提琴
const fakeData = [{
id: 1,
name: 'foo'
},
{
id: 2,
name: 'bar'
},
{
id: 3,
name: 'baz'
}
];
let idsToFetch = [];
let getItemsPromise, resolve, reject;
const fetchItems = _.debounce(() => {
console.log('fetching items...');
const currentResolve = resolve;
const currentReject = reject;
// simulating ajax request
setTimeout(function() {
const result = idsToFetch.map((id) => fakeData.find(item => item.id == id));
currentResolve(result);
}, 400);
getItemsPromise = resolve = reject = null;
}, 500);
function getItems(ids) {
idsToFetch = ids.filter((id) => !idsToFetch.includes(id)).concat(idsToFetch);
if (!getItemsPromise) {
getItemsPromise = new Promise((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
}
fetchItems();
return getItemsPromise
.then((res) => {
return res.filter((item) => ids.includes(item.id));
})
}
setTimeout(() => {
console.log('first request start');
getItems([1]).then(res => console.log('first result:', res));
}, 100);
setTimeout(() => {
console.log('second request start');
getItems([1, 2]).then(res => console.log('second result:', res));
}, 200)
setTimeout(() => {
console.log('third request start');
getItems([1, 3]).then(res => console.log('third result:', res));
}, 300)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
解决方案
我发现编写一个debounce()
行为完全受我控制的函数比依赖库方法更简单。
特别是,我不遗余力地设计了与问题中的不同的去抖行为,其中(如果我理解正确的话)第一个,也可能只有一个,快速序列的请求必须等待去抖延迟到期。
在下面的代码中,使用 a 代替原来getItemsPromise
的 adebouncePromise
来表示去抖动周期,在此期间抑制获取并允许累积请求数据。从静止状态(无论何时debouncePromise === null
),下一次fetch()
调用将尽快获取数据(下一个滴答声)。只有第二次和后续调用会被去抖动,直到去抖动期到期并且debounce()
实例返回到其静止状态。我认为这与原始范式一样有效,并且可以说是更好的“去抖动”范式。(如果没有,那么fetch()
可以轻轻修改以提供原始行为)。
除此之外,差异很小:
- 凌乱的外化
resolve
和reject
避免 - 为了保持
debounce()
通用性,resultsFilter
除了 afetcher
和之外,还传递了一个函数delay
。
代码中的进一步注释。
function debounce(fetcher, resultsFilter, delay) {
let idsToFetch = [],
debouncePromise = null;
function reset() { // utility funtion - keeps code below clean and DRY
let idsToFetch_ = idsToFetch;
idsToFetch = [];
return idsToFetch_;
}
function fetch(ids) {
idsToFetch = idsToFetch.concat(ids.filter(id => !idsToFetch.includes(id))); // swapped around so as not to reverse the order.
if (!debouncePromise) {
// set up the debounce period, and what is to happen when it expires.
debouncePromise = new Promise(resolve => {
setTimeout(resolve, delay);
}).then(() => {
// on expiry of the debounce period ...
debouncePromise = null; // ... return to quiescent state.
return fetcher(reset()); // ... fetch (and deliver) data for all request data accumulated in the debounce period.
});
// *** First call of this debounce period - FETCH IMMEDIATELY ***
return Promise.resolve(reset()).then(fetcher); // (1) ensure fetcher is called asynchronously (as above). (2) resultsFilter is not necessary here.
} else {
return debouncePromise.then(res => resultsFilter(ids, res)); // when debouncePromise exists, return it with chained filter to give only the results for these ids.
}
}
return fetch;
}
示例用法:
function fetchItems(ids) {
const fakeData = [
{ 'id': 1, 'name': 'foo' },
{ 'id': 2, 'name': 'bar' },
{ 'id': 3, 'name': 'baz' },
{ 'id': 4, 'name': 'zaz' }
];
if (ids.length > 0) {
return new Promise(resolve => { // simulate ajax request
setTimeout(resolve, 400);
}).then(() => {
return ids.map(id => fakeData.find(item => item.id == id));
});
} else {
return Promise.resolve([]);
}
}
function filterResults(ids, results) {
return results.filter(item => ids.includes(item.id));
}
// ******************************************************
let getItems = debounce(fetchItems, filterResults, 500);
// ******************************************************
setTimeout(() => {
console.log('first request start');
getItems([1]).then(res => console.log('first result:', res));
}, 100);
setTimeout(() => {
console.log('second request start');
getItems([1, 2]).then(res => console.log('second result:', res));
}, 200);
setTimeout(() => {
console.log('third request start');
getItems([1, 3]).then(res => console.log('third result:', res));
}, 300);
setTimeout(() => {
console.log('fourth request start');
getItems([1, 4]).then(res => console.log('fourth result:', res));
}, 2000);
推荐阅读
- typescript - IONIC 4,可观察订阅正在复制我的 html 模板
- javascript - 查找给定数组中重复数字的计数并将输出作为对象返回
- python - 无法从 Ubuntu 18.04 上的 URL 或 IP 访问 Django 运行服务器
- mysql - 是否可以以交替顺序输出两个子查询的联合结果?
- javascript - 用户使用node js和firebase登录时如何增加令牌的到期时间
- c# - 需要动态删除文件名中的重复文本
- php - 如何使用 SQL 和 PHP 首先显示最新上传的帖子?
- ios - 使用来自 youtube 自适应格式的 url 进行 AVAssest 初始化需要时间来加载视频
- html - 标签在没有 JS 的情况下充当表单上的按钮
- php - Laravel如何在自动重定向验证后发送参数以查看