摘要:用戶量量大,數(shù)據(jù)量大,而且要求實時更新數(shù)據(jù)的時候,需要使用。該方法接收的有兩種,一種是數(shù)組。是歷史數(shù)據(jù),時間段的數(shù)據(jù),根據(jù)時間顆粒來劃分。
1、websocket
用戶量量大,數(shù)據(jù)量大,而且要求實時更新數(shù)據(jù)的時候,需要使用websocket。
tradingview正好就是這樣的應(yīng)用場景。
getBars方法。tradingview圖表插件響應(yīng)用戶操作,根據(jù)用戶界面渲染需要的數(shù)據(jù)時間段,調(diào)用getBars方法,傳遞參數(shù)function(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback),我們需要構(gòu)造出預(yù)期的參數(shù)返回給onLoadedCallback方法,即onLoadedCallback(data)。
onLoadedCallback方法。該方法接收的data有兩種,一種是list數(shù)組。另一種是newBars對象。list是歷史數(shù)據(jù),rangeStartDate - rangeEndDate時間段的數(shù)據(jù),根據(jù)時間顆粒resolution來劃分。newBars是增量數(shù)據(jù),同樣是rangeStartDate - rangeEndDate時間段的數(shù)據(jù),但是這個數(shù)據(jù)必定是在當(dāng)前的resolution內(nèi)。當(dāng)我們把數(shù)據(jù)返回給onLoadedCallback,圖表會渲染這些數(shù)據(jù)。
3、數(shù)據(jù)獲取和填充頁面初始化,實例化圖表,連接websocket。
從websocket得到的數(shù)據(jù)存入緩存cacheData。
因為getBars是圖表自己調(diào)用的,所以當(dāng)我們存入數(shù)據(jù)到cacheData的時候,在getBars方法中調(diào)用onLoadedCallback就可以。
4、實例圖表調(diào)用的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]){ //如果緩存沒有數(shù)據(jù),而且未發(fā)出請求,記錄當(dāng)前節(jié)點開始時間 this.cacheData[tickerload] = rangeStartDate; //發(fā)起請求,從websocket獲取當(dāng)前時間段的數(shù)據(jù) this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback); //設(shè)置狀態(tài)為true this.cacheData[tickerstate] = !0; return false; } if(!this.cacheData[tickerload] || this.cacheData[tickerload] > rangeStartDate){ //如果緩存有數(shù)據(jù),但是沒有當(dāng)前時間段的數(shù)據(jù),更新當(dāng)前節(jié)點時間 this.cacheData[tickerload] = rangeStartDate; //發(fā)起請求,從websocket獲取當(dāng)前時間段的數(shù)據(jù) this.initMessage(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback); //設(shè)置狀態(tài)為true this.cacheData[tickerstate] = !0; return false; } if(this.cacheData[tickerstate]){ //正在從websocket獲取數(shù)據(jù),禁止一切操作 return false; } //ticker = this.symbol + "-" + this.interval; //如果緩存有數(shù)據(jù),且長度不為0,構(gòu)造數(shù)據(jù) 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 { //如果沒有數(shù)據(jù),等待10ms,再執(zhí)行一次 var self = this this.getBarTimer = setTimeout(function() { self.getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback) }, 10) } }
獲取歷史數(shù)據(jù)list的init方法:
TVjsApi.prototype.initMessage = function(symbolInfo, resolution, rangeStartDate, rangeEndDate, onLoadedCallback){ //console.log("發(fā)起請求,從websocket獲取當(dāng)前時間段的數(shù)據(jù)"); var that = this; //保留當(dāng)前回調(diào) that.cacheData["onLoadedCallback"] = onLoadedCallback; //獲取需要請求的數(shù)據(jù)數(shù)目 var limit = that.initLimit(resolution, rangeStartDate, rangeEndDate); //商品名 var symbol = that.symbol; //如果當(dāng)前時間顆粒已經(jīng)改變,停止上一個時間顆粒的訂閱,修改時間節(jié)點值 if(that.interval !== resolution){ that.unSubscribe(that.interval) that.interval = resolution; } //獲取當(dāng)前時間段的數(shù)據(jù),在onMessage中執(zhí)行回調(diào)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() }) } }
響應(yīng)websocket的Message方法:
TVjsApi.prototype.onMessage = function(data) { var thats = this; // console.log("這是后臺返回的數(shù)據(jù)"+count+":"+JSON.stringify(data) ) if (data.data && data.data.length) { //websocket返回的值,數(shù)組代表時間段歷史數(shù)據(jù),不是增量 var list = [] var ticker = thats.symbol + "-" + thats.interval; var tickerstate = ticker + "state"; //var that = thats; //遍歷數(shù)組,構(gòu)造緩存數(shù)據(jù) 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) //如果沒有緩存數(shù)據(jù),則直接填充,發(fā)起訂閱 if(!thats.cacheData[ticker]){ /*thats.cacheData[ticker] = thats.cacheData[ticker].concat(list); thats.cacheData["onLoadedCallback"](list); }else{*/ thats.cacheData[ticker] = list; thats.subscribe() } //新數(shù)據(jù)即當(dāng)前時間段需要的數(shù)據(jù),直接喂給圖表插件 if(thats.cacheData["onLoadedCallback"]){ thats.cacheData["onLoadedCallback"](list); } //請求完成,設(shè)置狀態(tài)為false thats.cacheData[tickerstate] = !1; //記錄當(dāng)前緩存時間,即數(shù)組最后一位的時間 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,即返回的是訂閱數(shù)據(jù), //緩存的key var ticker = thats.symbol + "-" + thats.interval; //構(gòu)造增量更新數(shù)據(jù) 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 }*/ //如果增量更新數(shù)據(jù)的時間大于緩存時間,而且緩存有數(shù)據(jù),數(shù)據(jù)長度大于0 if (barsData.time > thats.lastTime && thats.cacheData[ticker] && thats.cacheData[ticker].length) { //增量更新的數(shù)據(jù)直接加入緩存數(shù)組 thats.cacheData[ticker].push(barsData) //修改緩存時間 thats.lastTime = barsData.time }else if(barsData.time == thats.lastTime && thats.cacheData[ticker].length){ //如果增量更新的時間等于緩存時間,即在當(dāng)前時間顆粒內(nèi)產(chǎn)生了新數(shù)據(jù),更新當(dāng)前數(shù)據(jù) thats.cacheData[ticker][thats.cacheData[ticker].length - 1] = barsData } // 通知圖表插件,可以開始增量更新的渲染了 thats.datafeeds.barsUpdater.updateData() } }
代碼邏輯如下:
getBars方法由圖表插件調(diào)用,帶有當(dāng)前需要渲染的時間參數(shù)、時間顆粒和callback。
如果緩存中有當(dāng)前時間段的數(shù)據(jù),構(gòu)造newBars,調(diào)用onLoadedCallback(newBars)。
如果緩存中沒有數(shù)據(jù),判斷當(dāng)前需要的數(shù)據(jù)類型,如果是list歷史記錄,那么我們需要通過websocket來獲取,執(zhí)行initMessage。
判斷當(dāng)前緩存中的時間顆粒resolution是否已經(jīng)改變,如果已經(jīng)改變,需要清除緩存數(shù)據(jù),更新緩存cacheData = list,返回圖表onLoadedCallback(list),另外需要訂閱當(dāng)前時間顆粒的增量數(shù)據(jù),取消上一次訂閱。
如果時間顆粒resolution沒有改變,websocket得到的歷史數(shù)據(jù)不需要存入緩存,直接調(diào)用onLoadedCallback(list)就可以,因為圖表插件的渲染是根本得到的數(shù)據(jù)時間段來執(zhí)行的,即:圖表插件會緩存已經(jīng)渲染過的數(shù)據(jù)。
如果當(dāng)前請求的數(shù)據(jù)為增量數(shù)據(jù),但是緩存中沒有,等待10ms,因為增量數(shù)據(jù)來源于websocket,我們已經(jīng)訂閱,需要等待websocket返回,相當(dāng)于pending。
以上邏輯基本含括了用戶的兩個重要操作:切換時間顆粒、查看歷史記錄。
可運行代碼在GitHub上已經(jīng)更新,可預(yù)覽。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99049.html
摘要:無奈,還是需要對這份代碼進行加工。功能缺少,主要指業(yè)務(wù)邏輯實現(xiàn)上的功能缺少。缺少的功能主要是歷史記錄獲取展示的功能。查詢緩存是否為空,如果為空,表示數(shù)據(jù)還沒有下發(fā),后再查詢一次。如果有數(shù)據(jù),取到當(dāng)前數(shù)據(jù),執(zhí)行回調(diào)。 前幾天寫了一篇關(guān)于tradingView和webSocket的文章傳送門,因為代碼本身還在整合中,所以比較混亂,而且也沒有demo可以運行。這兩天在GitHub上面看到了一...
摘要:進階期理解中的執(zhí)行上下文和執(zhí)行棧進階期深入之執(zhí)行上下文棧和變量對象但是今天補充一個知識點某些情況下,調(diào)用堆棧中函數(shù)調(diào)用的數(shù)量超出了調(diào)用堆棧的實際大小,瀏覽器會拋出一個錯誤終止運行。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,今天是第3天。 本計劃一共28期,每期重點攻...
摘要:本計劃一共期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃,點擊查看前端進階的破冰之旅本期推薦文章深入之執(zhí)行上下文棧和深入之變量對象,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,今天是第二天。 本計劃一共28期,每期...
摘要:使用上一篇文章的例子來說明下自由變量進階期深入淺出圖解作用域鏈和閉包訪問外部的今天是今天是其中既不是參數(shù),也不是局部變量,所以是自由變量。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第二期,本周的主題是作用域閉包,今天是第7天。 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進階計...
摘要:首次運行代碼時,會創(chuàng)建一個全局執(zhí)行上下文并到當(dāng)前的執(zhí)行棧中。執(zhí)行上下文的創(chuàng)建執(zhí)行上下文分兩個階段創(chuàng)建創(chuàng)建階段執(zhí)行階段創(chuàng)建階段確定的值,也被稱為。 (關(guān)注福利,關(guān)注本公眾號回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進階的第一期,本周的主題是調(diào)用堆棧,,今天是第一天 本計劃一共28期,每期重點攻克一個面試重難點,如果你還不了解本進...
閱讀 3679·2021-11-24 09:38
閱讀 3159·2021-11-15 11:37
閱讀 801·2021-11-12 10:36
閱讀 3557·2021-10-21 09:38
閱讀 3230·2021-09-28 09:36
閱讀 2430·2021-09-22 16:01
閱讀 5006·2021-09-22 15:09
閱讀 1230·2019-08-30 15:55