用户量量大,数据量大,并且要求实时更新数据的时候,须要使用websocket。
tradingview正好就是这样的应用场景。html
图表调用的getBars方法:git
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方法:github
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方法:web
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() } }
代码逻辑以下:数组
以上逻辑基本含括了用户的两个重要操做:切换时间颗粒、查看历史记录。
可运行代码在GitHub上已经更新,可预览。缓存