Vue 源碼思想在工作中的應用
一、背景
由于電腦CPU、內存等的限制,能夠同時啟動的任務數有一定限制,例如一臺電腦能夠執行5個異步任務,但是目前有100個異步任務要執行,那么如何讓這100個任務無間隔的快速執行完畢呢?
二、問題解答
剛遇到這個問題的時候,也是出于懵逼狀態,怎么處理呢???碰巧的是近期學習了一些Vue的源碼知識,那么是不是可以借鑒其思想來解決遇到的這個難題呢?經過一步步分析,確定答案是肯定的。下面從解題思路、知識點及代碼實現來聊一聊實現過程。
2.1 解題思路
上述是整個流程圖,其流程可簡化為以下幾個步驟:
將所有任務分為兩組,任務組1指的是電腦可以并行執行的異步任務,任務組2指的是其余的異步任務;
將任務組1變為可幀聽狀態,即其發生變化時我們能夠知道;
將任務組1中的任務觸發(不觸發任務不會執行);
某一任務執行完畢后,將任務組2中的一個任務填充到可幀聽狀態的任務組1中;
任務按照固定數量不斷執行,直到所有任務執行完畢。
2.2 知識點
在Vue源碼中,Vue2.x使用Object.defineProperty()實現對數據的幀聽;Vue3.0使用Proxy實現對數據的幀聽。本著趕時髦和Proxy確實優秀的態度,在實現過程中也應用了Proxy。Proxy作為一個新的知識點,先了解一下其定義及使用方法。
2.2.1 定義
Proxy中文意思是“代理”,是在目標對象之間架設一層“攔截”,從而可以修改某些操作的默認行為。Proxy共支持十三種攔截操作:get、set、has、deleteProperty、ownKeys、getOwnPropertyDescriptor、defineProperty、preventExtensions、getPrototypeOf、isExtensible、setPrototypeOf、apply、construct。
2.2.2 簡單使用
- function testProxy(obj) {
- return new Proxy(obj, {
- get: (target, key) => {
- console.log(`我被get攔截器攔截了,攔截的屬性是${key}`);
- },
- set: (target, key, value) => {
- console.log(`我被set攔截器攔截了,攔截的屬性是${key}, 新值是${value}`);
- target[key] = value;
- }
- });
- }
- const testObj = {
- a: 10
- };
- const proxy = testProxy(testObj);
- proxy.a = 100;
2.2.3 詳細使用
詳細用法可以參考阮一峰大佬的“ECMAScript 6入門”。
2.3 代碼實現
2.3.1 定義兩個任務隊列
首先定義兩個任務隊列,task1為開始執行的一批任務,task2中為后續添加進去的任務。
- const task1 = [1, 2, 3];
- const task2 = [4, 5, 6, 7, 8];
2.3.2 數據變為可幀聽的函數
利用Proxy將數據變為可幀聽狀態
- /**
- * 監聽模塊,監聽對應數組的變化,保證其始終有一定長度的內容在運行
- *
- * @param {Array} initArr 定長任務的數組
- * @param {Function} callback 對應的回調函數
- */
- function watcher(initArr, callback) {
- const proxy = new Proxy(initArr, {
- set(target, key, value, receiver) {
- target[key] = value;
- callback(value, key, receiver);
- }
- });
- return proxy;
- }
2.3.3 異步任務邏輯
- /**
- * 異步任務的運行邏輯
- *
- * @param {number} taskIndex 異步任務的序號
- * @param {number} index 當前任務在定長任務的序號
- * @param {Proxy} proxy Proxy實例
- */
- function asyncTask(taskIndex, index, proxy) {
- console.log(`${index}索引處的任務${taskIndex}開始執行`);
- return new Promise(resolve => {
- setTimeout(() => {
- console.log(`${index}索引處的任務${taskIndex}執行完畢`);
- // 當任務隊列2中還有任務時,進入隊列替換任務1中執行完的任務
- if (task2.length > 0) {
- proxy[index] = task2.shift();
- }
- }, 1000 + 2000 * Math.random());
- });
- }
2.3.4 主函數
- const proxy = watcher(task1, asyncTask);
- task1.forEach((taskIndex, index) => asyncTask(taskIndex, index, proxy));
- }
2.3.5 執行結果
通過結果可以看到,當一個任務完成時立刻將有一個新的任務進入。
- 0索引處的任務1開始執行
- 1索引處的任務2開始執行
- 2索引處的任務3開始執行
- 0索引處的任務1執行完畢
- 0索引處的任務4開始執行
- 2索引處的任務3執行完畢
- 2索引處的任務5開始執行
- 1索引處的任務2執行完畢
- 1索引處的任務6開始執行
- 0索引處的任務4執行完畢
- 0索引處的任務7開始執行
- 2索引處的任務5執行完畢
- 2索引處的任務8開始執行
- 1索引處的任務6執行完畢
- 0索引處的任務7執行完畢
- 2索引處的任務8執行完畢
三、討論
針對此類問題的處理方式,各位大佬有新的思路與方法歡迎留言,我們一起討論,共同進步。
本文轉載自微信公眾號「前端點線面」,可以通過以下二維碼關注。轉載本文請聯系前端點線面公眾號。