在我们平时开发中,经常会遇到页面数据初始化时,频繁调同一个接口的情况。比如echarts项目中,一个页面可能会有几十张图表,如果一个接口返回所有图表数据的话,会造成用户过长的等待时间,再者过多图表同时渲染,也会给页面增加压力,造成卡顿的现象。
我们通常会让每个图表单独调一个接口,入参不同,这样更有利于页面快速渲染图表,单个图表请求到数据,立即渲染,不需要等待其他图表。可理想很丰满,现实很骨感,当服务器配置过低,或者后端代码性能较弱,会难以处理这些并发请求,接口调用越多,等待处理的时间可能就越长,甚至超过一次性返回所有数据的间。。。为了解决这种问题,缓解后端压力,本篇将介绍前端来控制请求的并发数:
先分析一波,假设我们需要重复调用30次接口,并联调用接口,服务端压力较大,可能会造成响应时间过长。逐渐减少并发数,假设并发数为5的时候,服务器处理速度最快,几乎不受并发影响。
针对这种情况,我们可以封装接口请求方法,控制每次接口请求的并发数,将30次分解成:并发数为5,分6次请求。这样的话,服务器每次处理5次请求,资源释放出来继续处理下一批请求,从而解决并发拥堵问题~
初步构思:
class TaskQueue {
constructor(max) {
this.max = max;
this.taskList = [];
}
addTask(task) {
this.taskList.push(task);
}
}
function createTask(i) {
return () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (i == 4 || i == 15) {
reject("出错啦~");
} else {
resolve("成功呀~" + i);
}
}, 2000);
});
};
}
const taskQueue = new TaskQueue(5);
for (let i = 0; i < 30; i++) {
const task = createTask(i);
taskQueue.addTask(task);
}
for循环调用函数createTask()返回30个promise的异步任务,任务队列TaskQueue类返回一个实例,控制这30个异步任务的并发,构造器中传入并发数5。
接下来用TaskQueue实现控制并发:
class TaskQueue {
constructor(max) {
this.max = max; // 并发数
this.min = 0;
this.taskList = []; // 全部任务
Promise.resolve().then(() => this.run()) // 等同步代码(addTask)全部执行完成,再执行run
}
// 增加任务
addTask(task) {
this.taskList.push(task);
}
// 执行任务
async run() {
if (!this.taskList.length) return;
const AsyncTasks = [];
this.min = Math.min(this.max, this.taskList.length) // 当传入的并发数大于任务数,取任务数, 反之取并发数
// 根据并发数分组
for(let i = 0; i < this.min; i++) {
AsyncTasks.push(this.taskList.shift());
}
await this.handleTask(AsyncTasks); // 通过下面递归,这里将会有6个异步任务串联执行
this.run(); // 递归
}
async handleTask(tasks) {
// 返回promise处理异步任务组
return new Promise(resolve => {
// 遍历任务组,5个异步任务并联执行
tasks.forEach(async (task, index) => {
await task().then(res => {
console.log(res);
}).catch((err) => {
console.log(err);
}).finally(() => {
index + 1 === this.min && console.log('===============================');
index + 1 === this.min && resolve() // 最后一个任务resolve(),promise完成
})
})
})
}
}
function createTask(i) {
return () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (i == 4 || i == 15) { // 测试捕捉错误
reject("出错啦~");
} else {
resolve("成功呀~" + i);
}
}, 2000);
});
};
}
const taskQueue = new TaskQueue(5);
for (let i = 0; i < 30; i++) {
const task = createTask(i);
taskQueue.addTask(task);
}
试试效果:
nice,至此,30次异步任务,分6次完成,每次处理5个,大家可以在此基础上拓展请求接口,并增加一些处理逻辑,欢迎留言探讨~
脚踏实地行,海阔天空飞~
在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
在我的Character模型中,我添加了:字符.rbbefore_savedoself.profile_picture_url=asset_path('icon.png')end但是,对于数据库中已存在的所有角色,它们的profile_picture_url为nil。因此,我想进入控制台并遍历所有这些并进行设置。在我试过的控制台中:Character.find_eachdo|c|c.profile_picture_url=asset_path('icon.png')end但这给出了错误:NoMethodError:undefinedmethod`asset_path'formain:O
当我进入Rails控制台时,我已将pry设置为加载代替irb。我找不到该页面或不记得如何将其恢复为默认行为,因为它似乎干扰了我的Rubymine调试器。有什么建议吗? 最佳答案 我刚发现问题,pry-railsgem。忘记了它的目的是让“railsconsole”打开pry。 关于ruby-on-rails-带有Pry的Rails控制台,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/question
我正在尝试将$stdout设置为临时写入一个文件,然后返回到一个文件。test.rb:old_stdout=$stdout$stdout.reopen("mytestfile.out",'w+')puts"thisgoesinmytestfile"$stdout=old_stdoutputs"thisshouldbeontheconsole"$stdout.reopen("mytestfile1.out",'w+')puts"thisgoesinmytestfile1:"$stdout=old_stdoutputs"thisshouldbebackontheconsole"这是输出。r
我在思考流量控制的最佳实践。我应该走哪条路?1)不要检查任何东西并让程序失败(更清晰的代码,自然的错误消息):defself.fetch(feed_id)feed=Feed.find(feed_id)feed.fetchend2)通过返回nil静默失败(但是,“CleanCode”说,你永远不应该返回null):defself.fetch(feed_id)returnunlessfeed_idfeed=Feed.find(feed_id)returnunlessfeedfeed.fetchend3)抛出异常(因为不按id查找feed是异常的):defself.fetch(feed_id