摘要:用户量量大,数据量大,而且要求实时更新数据的时候,需要使用。该方法接收的有两种,一种是数组。是历史数据,时间段的数据,根据时间颗粒来划分。
1、websocket
用户量量大,数据量大,而且要求实时更新数据的时候,需要使用websocket。
tradingview正好就是这样的应用场景。
getBars方法。tradingview图表插件响应用户操作,根据用户界面渲染需要的数据时间段,调用getBars方法,传递参数function(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback),我们需要构造出预期的参数返回给onLoadedCallback方法,即onLoadedCallback(data)。
onLoadedCallback方法。该方法接收的data有两种,一种是list数组。另一种是newBars对象。list是历史数据,rangeStartDate - rangeEndDate时间段的数据,根据时间颗粒resolution来划分。newBars是增量数据,同样是rangeStartDate - rangeEndDate时间段的数据,但是这个数据必定是在当前的resolution内。当我们把数据返回给onLoadedCallback,图表会渲染这些数据。
3、数据获取和填充页面初始化,实例化图表,连接websocket。
从websocket得到的数据存入缓存cacheData。
因为getBars是图表自己调用的,所以当我们存入数据到cacheData的时候,在getBars方法中调用onLoadedCallback就可以。
4、实例图表调用的getBars方法:
TVjsApi.prototype.getBars = function(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) { //console.log(" >> :", this.formatt(rangeStartDate*1000), this.formatt(rangeEndDate*1000)) var ticker = this.symbol + "-" + resolution; var tickerload = ticker + "load"; var tickerstate = ticker + "state"; if(!this.cacheData[ticker] && !this.cacheData[tickerstate]){ //如果缓存没有数据,而且未发出请求,记录当前节点开始时间 this.cacheData[tickerload] = rangeStartDate; //发起请求,从websocket获取当前时间段的数据 this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback); //设置状态为true this.cacheData[tickerstate] = !0; return false; } if(!this.cacheData[tickerload] || this.cacheData[tickerload] > rangeStartDate){ //如果缓存有数据,但是没有当前时间段的数据,更新当前节点时间 this.cacheData[tickerload] = rangeStartDate; //发起请求,从websocket获取当前时间段的数据 this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback); //设置状态为true this.cacheData[tickerstate] = !0; return false; } if(this.cacheData[tickerstate]){ //正在从websocket获取数据,禁止一切操作 return false; } //ticker = this.symbol + "-" + this.interval; //如果缓存有数据,且长度不为0,构造数据 if (this.cacheData[ticker] && this.cacheData[ticker].length) { this.isLoading = false var newBars = [] this.cacheData[ticker].forEach(item => { if (item.time >= rangeStartDate * 1000 && item.time <= rangeEndDate * 1000) { newBars.push(item) } }) onLoadedCallback(newBars) } else { //如果没有数据,等待10ms,再执行一次 var self = this this.getBarTimer = setTimeout(function() { self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) }, 10) } }
获取历史数据list的init方法:
TVjsApi.prototype.initMessage = function(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback){ //console.log("发起请求,从websocket获取当前时间段的数据"); var that = this; //保留当前回调 that.cacheData["onLoadedCallback"] = onLoadedCallback; //获取需要请求的数据数目 var limit = that.initLimit(resolution, rangeStartDate, rangeEndDate); //商品名 var symbol = that.symbol; //如果当前时间颗粒已经改变,停止上一个时间颗粒的订阅,修改时间节点值 if(that.interval !== resolution){ that.unSubscribe(that.interval) that.interval = resolution; } //获取当前时间段的数据,在onMessage中执行回调onLoadedCallback if (that.interval < 60) { that.socket.send({ cmd: "req", args: ["candle.M"+resolution+"."+symbol, limit, rangeEndDate], id: "trade.M"+ resolution +"."+ symbol.toLowerCase() }) } else if (that.interval >= 60) { that.socket.send({ cmd: "req", args: ["candle.H"+(resolution/60)+"."+symbol, limit, rangeEndDate], id: "trade.H"+ (resolution / 60) +"."+ symbol.toLowerCase() }) } else { that.socket.send({ cmd: "req", args: ["candle.D1."+symbol, limit, rangeEndDate], id: "trade.D1."+ symbol.toLowerCase() }) } }
响应websocket的Message方法:
TVjsApi.prototype.onMessage = function(data) { var thats = this; // console.log("这是后台返回的数据"+count+":"+JSON.stringify(data) ) if (data.data && data.data.length) { //websocket返回的值,数组代表时间段历史数据,不是增量 var list = [] var ticker = thats.symbol + "-" + thats.interval; var tickerstate = ticker + "state"; //var that = thats; //遍历数组,构造缓存数据 data.data.forEach(function(element) { list.push({ time: element.id*1000, open: element.open, high: element.high, low: element.low, close: element.close, volume: element.quote_vol }) }, thats) //如果没有缓存数据,则直接填充,发起订阅 if(!thats.cacheData[ticker]){ /*thats.cacheData[ticker] = thats.cacheData[ticker].concat(list); thats.cacheData["onLoadedCallback"](list); }else{*/ thats.cacheData[ticker] = list; thats.subscribe() } //新数据即当前时间段需要的数据,直接喂给图表插件 if(thats.cacheData["onLoadedCallback"]){ thats.cacheData["onLoadedCallback"](list); } //请求完成,设置状态为false thats.cacheData[tickerstate] = !1; //记录当前缓存时间,即数组最后一位的时间 thats.lastTime = thats.cacheData[ticker][thats.cacheData[ticker].length - 1].time } if (data.type && data.type.indexOf(thats.symbol.toLowerCase()) !== -1) { // console.log(" >> sub:", data.type) // data带有type,即返回的是订阅数据, //缓存的key var ticker = thats.symbol + "-" + thats.interval; //构造增量更新数据 var barsData = { time: data.id * 1000, open: data.open, high: data.high, low: data.low, close: data.close, volume: data.quote_vol } /*if (barsData.time >= thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) { thats.cacheData[ticker][thats.cacheData[ticker].length - 1] = barsData }*/ //如果增量更新数据的时间大于缓存时间,而且缓存有数据,数据长度大于0 if (barsData.time > thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) { //增量更新的数据直接加入缓存数组 thats.cacheData[ticker].push(barsData) //修改缓存时间 thats.lastTime = barsData.time }else if(barsData.time == thats.lastTime && thats.cacheData[ticker].length){ //如果增量更新的时间等于缓存时间,即在当前时间颗粒内产生了新数据,更新当前数据 thats.cacheData[ticker][thats.cacheData[ticker].length - 1] = barsData } // 通知图表插件,可以开始增量更新的渲染了 thats.datafeeds.barsUpdater.updateData() } }
代码逻辑如下:
getBars方法由图表插件调用,带有当前需要渲染的时间参数、时间颗粒和callback。
如果缓存中有当前时间段的数据,构造newBars,调用onLoadedCallback(newBars)。
如果缓存中没有数据,判断当前需要的数据类型,如果是list历史记录,那么我们需要通过websocket来获取,执行initMessage。
判断当前缓存中的时间颗粒resolution是否已经改变,如果已经改变,需要清除缓存数据,更新缓存cacheData = list,返回图表onLoadedCallback(list),另外需要订阅当前时间颗粒的增量数据,取消上一次订阅。
如果时间颗粒resolution没有改变,websocket得到的历史数据不需要存入缓存,直接调用onLoadedCallback(list)就可以,因为图表插件的渲染是根本得到的数据时间段来执行的,即:图表插件会缓存已经渲染过的数据。
如果当前请求的数据为增量数据,但是缓存中没有,等待10ms,因为增量数据来源于websocket,我们已经订阅,需要等待websocket返回,相当于pending。
以上逻辑基本含括了用户的两个重要操作:切换时间颗粒、查看历史记录。
可运行代码在GitHub上已经更新,可预览。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/99049.html
摘要:无奈,还是需要对这份代码进行加工。功能缺少,主要指业务逻辑实现上的功能缺少。缺少的功能主要是历史记录获取展示的功能。查询缓存是否为空,如果为空,表示数据还没有下发,后再查询一次。如果有数据,取到当前数据,执行回调。 前几天写了一篇关于tradingView和webSocket的文章传送门,因为代码本身还在整合中,所以比较混乱,而且也没有demo可以运行。这两天在GitHub上面看到了一...
摘要:进阶期理解中的执行上下文和执行栈进阶期深入之执行上下文栈和变量对象但是今天补充一个知识点某些情况下,调用堆栈中函数调用的数量超出了调用堆栈的实际大小,浏览器会抛出一个错误终止运行。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第3天。 本计划一共28期,每期重点攻...
摘要:本计划一共期,每期重点攻克一个面试重难点,如果你还不了解本进阶计划,点击查看前端进阶的破冰之旅本期推荐文章深入之执行上下文栈和深入之变量对象,由于微信不能访问外链,点击阅读原文就可以啦。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,今天是第二天。 本计划一共28期,每期...
摘要:使用上一篇文章的例子来说明下自由变量进阶期深入浅出图解作用域链和闭包访问外部的今天是今天是其中既不是参数,也不是局部变量,所以是自由变量。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第二期,本周的主题是作用域闭包,今天是第7天。 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进阶计...
摘要:首次运行代码时,会创建一个全局执行上下文并到当前的执行栈中。执行上下文的创建执行上下文分两个阶段创建创建阶段执行阶段创建阶段确定的值,也被称为。 (关注福利,关注本公众号回复[资料]领取优质前端视频,包括Vue、React、Node源码和实战、面试指导) 本周正式开始前端进阶的第一期,本周的主题是调用堆栈,,今天是第一天 本计划一共28期,每期重点攻克一个面试重难点,如果你还不了解本进...
阅读 3660·2021-11-24 09:38
阅读 3150·2021-11-15 11:37
阅读 787·2021-11-12 10:36
阅读 3553·2021-10-21 09:38
阅读 3223·2021-09-28 09:36
阅读 2425·2021-09-22 16:01
阅读 4996·2021-09-22 15:09
阅读 1218·2019-08-30 15:55