效能最佳化之使用vue-worker外掛(基於Web Worker)開啟多執行緒運算提高效率

水冗水孚發表於2023-02-14

什麼是Web Worker

15年前,也就是2008年,html第五版html5釋出,這一版的釋出,提供了不少新的好用的功能,如:

  • Canvas繪圖
  • 拖拽drag
  • websocket
  • Geolocation
  • webworker
  • 等...

筆者之前說過:一項新技術新的技術方案的提出,一定是為了解決某個問題的,或者是對某種方案的最佳化

那麼Web Worker這個新技術解決了什麼問題?有哪些最佳化價值呢?

讓我們繼續往下看...

官方定義

Web Worker 為 Web 內容在後臺執行緒中執行指令碼提供了一種簡單的方法。執行緒可以執行任務而不干擾使用者介面。此外,他們可以使用XMLHttpRequest執行 I/O (儘管responseXML和channel屬性總是為空)。一旦建立,一個 worker 可以將訊息傳送到建立它的 JavaScript 程式碼,透過將訊息釋出到該程式碼指定的事件處理程式(反之亦然)......

官方MDN地址:https://developer.mozilla.org...

乍一看這段話,好像懂了(Web Worker和執行緒有關),又好像沒懂。但是我們能看到一個加粗的關鍵字:執行緒

那麼,新的問題來了,什麼是執行緒?

當我們去學習一個知識點A的時候,我們會發現知識點A是由a1、a2、a3、a4組合而成的,所以我們要繼續下鑽,去搞懂a1、a2、a3、a4分別是什麼意思。這樣才能更好的理解知識點A的含義內容。

什麼是執行緒

執行緒基本八股文定義不贅述,大家可以將執行緒理解成為一個打工仔,每天的工作就是解析翻譯並執行程式碼,像搬磚一樣,不過一次只能搬一塊磚,後面有磚,不好意思,你等會兒,等我把這個磚搬完了再搬你們。

執行緒道人朗聲道:爾等磚頭列隊稍後,待老夫一塊塊搬!(js中的任務佇列

js單執行緒語言,生來便是!java平常寫程式碼也是單執行緒的操作,不過單執行緒有時候不太夠用,於是java中也提供了多執行緒的操作入口,比如:Thread類Runnable介面Callable介面;大多數語言都是類似的,python也是的啊,python寫程式碼平常也是單線程,另也提供了threading模組讓程式設計師可以去做多執行緒操作

為何js要設計成單執行緒呢?

  • 符合大致趨勢
  • 這與js的工作內容有關:js只是用來去做一些使用者互動,並呈現效果內容。
  • 試想,如果js是多執行緒,執行緒一將dom元素的背景色改成紅色,執行緒二將dom元素的背景色改為綠色,那麼,到底上紅色還是綠色呢?
  • 不過後來人們發現,某些情況下,前端如果能做多執行緒的操作,將會大大提升開發效率
  • 於是Web Worker就應運而生了
  • Web Worker可以建立另外的執行緒去做一些操作,操作不影響js主執行緒(比如UI渲染
  • 本來只有一個js主執行緒搬磚,現在又過來好幾個js輔助執行緒一塊幫忙搬磚,那肯定效率高啊!
有點道友問,那如果主執行緒操作dom,然後在Web worker建立的輔助執行緒中也去操作dom呢?最終結果聽誰的啊???回答:Web work建立的執行緒中沒有document全域性文件物件,所以無法操作dom,另外,也不會這樣做的
  • 大家這樣理解:在實際開發場景中Web Worker主要,多數,應用在前端一些複雜中運算
  • 而大家都知道一些複雜的運算,基本上交由後端去處理(實際上覆雜的運算操作,後端也會看情況去開啟多執行緒操作的)
  • 這樣說來,Web Worker的作用就是把後端進行的多執行緒操作運算拿到前端了
  • 工作中一些資料的加工、計算、組裝邏輯操作,常常是由後端來幹;但是在某些情況下,如果前端去做的話,效率或許更高一些

所以Web Worker這個新技術的價值是什麼呢?

Web worker建立輔助執行緒、幫助前端主執行緒進行復雜耗時的計算

一個人手不夠用,那就多搖幾個人。

Web worker建立的一些輔助執行緒,分別去幫主執行緒分擔一些複雜的、耗時的js運算,這樣的話,主執行緒後續的程式碼執行就不會阻塞,當輔助執行緒計算出複雜耗時運算結果後,再與主執行緒通訊,將計算出的結果告知主執行緒。

Web Worker新技術價值,簡而言之:提升前端程式碼運算執行效率

關於Web worker的原生語法,諸如:

// 建立一個Web worker
var myWorker = new Worker('worker.js');

// 使用Web worker傳送訊息
worker.postMessage(params)

// 使用Web worker接收訊息
worker.onmessage = function(e) {
    console.log(e.data)
}

// 等等...

Web worker的原生語法,筆者不贅述了,大家可自行去MDN上的Web Worker去檢視,

為什麼不說呢?因為我們工作中開發程式碼,基本上都是使用框架,在框架中直接寫原生的Web worker有許多的邊界異常或者其他的情況需要控制。以vue框架為例,我們不能直接寫Web Worker,需要使用Webpack中的worker-loader外掛去解析Web worker,並且在vue.config.js中去做相應配置。

嗯,有些麻煩。

在這個情況下,基於Web Worker進行封裝的外掛庫vue-worker,就閃亮登場了。

簡單、好用、便於理解

這裡不提Web worker的原生基本語法不是說大家不用看了,看還是要看的,只不過篇幅原因(懶),這裡就不贅述了。

Web Worker的應用場景

如果大家只是寫增刪改查的業務程式碼,Web Worker用的的確非常少

工作中那些需要進行復雜耗時的計算的邏輯程式碼,都可以由前端使用Web Worker進行計算。但是複雜耗時的計算基本上都是交由後端進行。這個普遍認知下導致其使用的比較少。

大概有以下場景:

  • 加密解密資料(加密解密筆者之前接觸的是NodeRSA、md5、crypto
  • 提前獲取資料,比如提前傳送ajax請求獲取介面的一些圖片、數值等相關資料
  • 將耗時運算交由Web Work處理(筆者之前做的就是這個)
不過使用的是vue-worker外掛(基於Web Worker封裝的外掛)

vue-worker(基於Web worker封裝的一款不錯的外掛)

當我們找到一款還不錯的外掛的時候,我們首先去npm官網上看看這個外掛的下載量如何,以及GitHubstar數量多少。

npm地址:https://www.npmjs.com/package...

vue-worker下載量截圖:

好少啊,一共也才不到3000次下載量,這裡的下載量少是因為,應用場景不多,所以大家使用的也是少,接下來談談自己曾經使用Web worker的這個場景

曾經應用場景

筆者之前做一個專案,頁面上有很多的輸入框,裡面的輸入框中需要填寫硬體採集的:壓力、溫度、比熱容、質量大小等很多引數,當點選計算按鈕時,會把這些引數進行計算,得出的結果,以供工作人員進行工作參考。

相當於一個計算器,只不過計算時間會很長

本來說是把這個計算過程交給後端計算,只不過當時後端的同事有事情,請假一週。但是工作不能停下啊

於是筆者就想能不能前端計算呢?

經過一系列調研(谷歌一下),找到了vue-worker外掛

最終是由筆者這個前端去做的這個計算,時間挺好,產品喜笑顏開

使用步驟

1.下載依賴包

cnpm i vue-worker --save

2.在入口檔案中引入

import Vue from 'vue'
import VueWorker from 'vue-worker' // Web worker外掛
Vue.use(VueWorker)

3.使用

接下來,筆者舉兩個例子,更加便於大家理解

案例一 主執行緒渲染dom、輔助執行緒進行計算

需求:

  • 點選按鈕進行計算,計算執行兩個UI操作
  • 第一個UI操作,計算斐波那契數fib(44)的值,且統計計算所用的時長,並寫入到頁面上
  • 第二個UI操作,每隔0.1秒,更新頁面上h2標籤的內容值
  • 要求兩個操作不阻塞,不能出現後一個UI操作要等待前一個UI操作的情況
  • 因為斐波那契是遞迴執行,是一個比較耗時的操作fib(44)
  • 那就想辦法不讓這個耗時的操作堵塞住任務佇列

我們使用vue-worker提供的方法:this.$worker.run(func, [args]?)

寫法如下:

html

<h1>開啟一個執行緒運算用$worker.run方法</h1>
<br />
<el-button
  @click="openOneThread"
  type="success"
  plain
  size="small"
  style="margin-bottom: 16px"
  >計算斐波那契數列值和用時,以及渲染頁面兩個任務</el-button
>
<div>
  斐波那契值為:<span class="bold">{{ fibRes }}</span>
  <i v-show="btnLoading" class="el-icon-loading"></i>
  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 執行用時:
  <i v-show="btnLoading" class="el-icon-loading"></i>
  <span class="bold">{{ fibTime }}</span>
  毫秒
</div>

js

// data
worker: null,
btnLoading: false,
fibNum: 44,
fibRes: null, // 斐波那契計算的結果
fibTime: null, // 斐波那契計算用時

// methods
openOneThread() {
  /* 第一個UI操作 */
  this.btnLoading = true;
  this.worker = this.$worker // this.$worker.run(func, [args]?) 一次性的,自動銷燬
    .run(
      (n) => {
        // 注意這裡的函式是內部另一個執行緒空間的,不能從外部引入過來(記憶體空間隔離)
        function fib(n) {
          if ((n == 1) | (n == 2)) {
            return 1;
          } else {
            return fib(n - 1) + fib(n - 2);
          }
        }
        let start = Date.now(); // console.time()和console.timeEnd()直接拿不到值,就換種方式
        let res = fib(n);
        let end = Date.now(); // window.performance.now()也不能用,因為沒window物件
        return [res, end - start]; // 返回陣列,第一項是fib(44)的值,第二項是fib(44)遞迴計算用時
      },
      [this.fibNum] // 引數,從這裡傳遞進去,陣列形式
    )
    .then((res) => {
      console.log("then", res); // 另一個執行緒執行完以後就能拿到結果了
      this.fibRes = res[0];
      this.fibTime = res[1];
      this.btnLoading = false;
    })
    .catch((err) => {
      console.log("err", err);
      this.btnLoading = false;
    });
  /* 第二個UI操作 */
  let h2Dom = this.$refs.renderH2;
  let n = 0;
  let timer = setInterval(() => {
    if (n >= 60) {
      clearInterval(timer);
    } else {
      n++;
      h2Dom.innerHTML = n;
    }
  }, 100);
  // 使用web worker外掛vue-work的確可以做到不阻塞
},

效果圖

案例二 開啟三個輔助執行緒進行計算看時間

案例一,已經可以看到Web Worker優勢了。接下來,我們再舉個例子。

需求:需要計算3個fib(30),如果我們使用Promise,寫法是這樣的:

Promise.all進行計算

async usePromiseFn() {
  function asyncOne() {
    let async1 = new Promise(async (resolve, reject) => {
      function fib(n) {
        if ((n === 1) | (n === 2)) {
          return 1;
        } else {
          return fib(n - 1) + fib(n - 1);
        }
      }
      resolve(fib(30));
    });
    return async1;
  }

  function asyncTwo() {
    let async2 = new Promise(async (resolve, reject) => {
      function fib(n) {
        if ((n === 1) | (n === 2)) {
          return 1;
        } else {
          return fib(n - 1) + fib(n - 1);
        }
      }
      resolve(fib(30));
    });
    return async2;
  }

  function asyncThree() {
    let async3 = new Promise(async (resolve, reject) => {
      function fib(n) {
        if ((n === 1) | (n === 2)) {
          return 1;
        } else {
          return fib(n - 1) + fib(n - 1);
        }
      }
      resolve(fib(30));
    });
    return async3;
  }

  console.time("使用Promise搭配aysnc和await");

  // let paramsArr = [asyncOne()]; // 計算一個大概在3秒左右(計算一次重新整理一次頁面,精確一些)
  let paramsArr = [asyncOne(), asyncTwo(), asyncThree()]; // 計算三個耗時任務大概在9秒左右

  let res = await Promise.all(paramsArr);
  console.timeEnd("使用Promise搭配aysnc和await");
  console.log("結果", res);
},
},

使用Promise.all方法去計算3個fib值,用時在9秒左右,的確是有些慢。那麼,我們使用Web Worker方式呢?

此外,使用Promise.all控制檯也會有警告提醒:[Violation] 'click' handler took 8822ms

意思是:click事件中執行的程式耗時過長,看到沒,如果使用js主執行緒去進行復雜計算,瀏覽器都看不下去了...

再一個,大家在Promise執行的時候,去選中頁面上的文字,發現選中不了!就像卡住了一樣!從這個方面也說明,js主執行緒不適合執行復雜的運算,阻塞...

Web Worker進行計算

這裡使用this.$worker.create方法,搭配this.worker2.postAll方法

程式碼寫法如下

  created() {
    // 1. 定義執行緒所用的訊息函式陣列
    const actions = [
      {
        message: "fn1", // message訊息與func函式執行為對映關係
        func: (params1, params2) => {
          console.log("params引數-->", params1, params2);
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          return fib(30);
        },
      },
      {
        message: "fn2",
        func: () => {
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          return fib(30);
        },
      },
      {
        message: "fn3",
        func: () => {
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          // throw "一個報錯掛了,其他的也跟著掛了,走.catch"; // 丟擲錯誤(的確很像Promise.all())
          return fib(30);
        },
      },
    ];
    // 2. 根據訊息函式陣列去create建立一個worker,並存到data變數中去,以便於後續隨時使用
    this.worker2 = this.$worker.create(actions);
  },
 

// 點選觸發noParamsFn方法執行
// 使用多個執行緒
noParamsFn() {
  this.loadingOne = true;
  console.time("多個執行緒計算用時1");
  this.worker2
    .postAll()
    .then((res) => {
      console.timeEnd("多個執行緒計算用時1");
      console.log("res", res); // 結果是一個陣列 [267914296, 433494437, 701408733]
      this.loadingOne = false;
    })
    .catch((err) => {
      console.timeEnd("多個執行緒計算用時1");
      console.log("err", err);
      this.loadingOne = false;
    });
},

我們看一下效果圖

看到沒有。只用了53毫秒

  • 使用主執行緒去計算3個fib(30)的值,需要將近9秒的時間
  • 而使用Web Worker去建立三個輔助執行緒分別去運算fib(30)所需要的時間,只需要50多毫秒

效能提升了100多倍!

這個案例才真正的體現了,Web Worker開啟多執行緒運算提高效率的強大!

大家可以去筆者的個人網站上去看完整的效果圖:http://ashuai.work:8888/#/myWork

Web WorkerHTML5提出的規範,主流瀏覽器都已經得到了相容。IE就忽略吧

附上案例完整程式碼

篇幅有限,一些vue-worker外掛常用的使用語法細節,寫在程式碼中了,大家複製貼上即可使用

<template>
  <div class="threadWrap">
    <h1>開啟一個執行緒運算用$worker.run方法</h1>
    <br />
    <el-button
      @click="openOneThread"
      type="success"
      plain
      size="small"
      style="margin-bottom: 16px"
      >計算斐波那契數列值和用時,以及渲染頁面兩個任務</el-button
    >
    <div>
      斐波那契值為:<span class="bold">{{ fibRes }}</span>
      <i v-show="btnLoading" class="el-icon-loading"></i>
      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 執行用時:
      <i v-show="btnLoading" class="el-icon-loading"></i>
      <span class="bold">{{ fibTime }}</span>
      毫秒
    </div>
    <br />
    <div class="UI">
      <span>不阻塞後續的程式碼執行:</span>
      <h2 ref="renderH2"></h2>
    </div>
    <br />
    <br />
    <h1>開啟多個執行緒使用$worker.create、postAll方法</h1>
    <br />
    <el-button
      @click="noParamsFn"
      type="success"
      plain
      size="small"
      style="margin-bottom: 12px"
      :loading="loadingOne"
      >不傳參都執行一次</el-button
    >
    <el-button
      @click="byMessageNameStrFn"
      type="success"
      plain
      size="small"
      style="margin-bottom: 12px"
      :loading="loadingTwo"
      >根據message的名字指定誰執行(字串形式)</el-button
    >
    <el-button
      @click="byMessageNameObjParamsFn"
      type="success"
      plain
      size="small"
      style="margin-bottom: 12px"
      :loading="loadingThree"
      >根據message的名字指定誰執行(物件形式可傳參)</el-button
    >
    <div class="info">F12請開啟控制檯檢視--></div>
    <br />
    <h1>不使用多執行緒,使用Promise.all太耗時啦!</h1>
    <br />
    <el-button
      @click="usePromiseFn"
      type="success"
      plain
      size="small"
      style="margin-bottom: 12px"
      >Promise執行多個任務</el-button
    >
    <div class="info">F12請開啟控制檯檢視--></div>
  </div>
</template>

<script>
export default {
  name: "myWorkName",
  data() {
    return {
      worker: null,
      btnLoading: false,
      fibNum: 44,
      fibRes: null,
      fibTime: null,
      /****/
      loadingOne: false,
      loadingTwo: false,
      loadingThree: false,
      worker2: null,
    };
  },
  methods: {
    /**
     * 需求:點選資料計算進行兩個操作
     *      第一個UI操作,計算斐波那契數fib(44)的值,且統計計算所用的時長,並寫入到頁面上
     *      第二個UI操作,每隔0.1秒,更新頁面上h2標籤的內容值
     *      要求兩個操作不阻塞,不能出現後一個UI操作要等待前一個UI操作的情況
     *      因為斐波那契是遞迴執行,是一個比較耗時的操作fib(44)約需要近8秒的計算時間
     * */
    openOneThread() {
      /* 第一個UI操作 */
      this.btnLoading = true;
      this.worker = this.$worker // this.$worker.run(func, [args]?) 一次性的,自動銷燬
        .run(
          (n) => {
            // 注意這裡的函式是內部另一個執行緒空間的,不能從外部引入過來(記憶體空間隔離)
            function fib(n) {
              if ((n == 1) | (n == 2)) {
                return 1;
              } else {
                return fib(n - 1) + fib(n - 2);
              }
            }
            let start = Date.now(); // console.time()和console.timeEnd()直接拿不到值,就換種方式
            let res = fib(n);
            let end = Date.now(); // window.performance.now()也不能用,因為沒window物件
            return [res, end - start]; // 返回陣列,第一項是fib(44)的值,第二項是fib(44)遞迴計算用時
          },
          [this.fibNum] // 引數,從這裡傳遞進去,陣列形式
        )
        .then((res) => {
          console.log("then", res); // 另一個執行緒執行完以後就能拿到結果了
          this.fibRes = res[0];
          this.fibTime = res[1];
          this.btnLoading = false;
        })
        .catch((err) => {
          console.log("err", err);
          this.btnLoading = false;
        });
      /* 第二個UI操作 */
      let h2Dom = this.$refs.renderH2;
      let n = 0;
      let timer = setInterval(() => {
        if (n >= 60) {
          clearInterval(timer);
        } else {
          n++;
          h2Dom.innerHTML = n;
        }
      }, 100);
      // 使用web worker外掛vue-work的確可以做到不阻塞
    },
    /**
     * 使用多個執行緒
     * */
    // 呼叫方式一 不傳參
    noParamsFn() {
      this.loadingOne = true;
      console.time("多個執行緒計算用時1");
      this.worker2
        .postAll()
        .then((res) => {
          console.timeEnd("多個執行緒計算用時1");
          console.log("res", res); // 結果是一個陣列 [267914296, 433494437, 701408733]
          this.loadingOne = false;
        })
        .catch((err) => {
          console.timeEnd("多個執行緒計算用時1");
          console.log("err", err);
          this.loadingOne = false;
        });
    },
    // 呼叫方式二 根據message的名字去指定誰(可多個)去執行 字串形式
    byMessageNameStrFn() {
      this.loadingTwo = true;
      console.time("多個執行緒計算用時2");
      this.worker2
        .postAll(["fn1", "fn3"]) // 這裡指定"fn1", "fn3"去執行
        .then((res) => {
          console.timeEnd("多個執行緒計算用時2");
          console.log("res", res); // 結果是一個陣列 [267914296, 701408733]
          this.loadingTwo = false;
        })
        .catch((err) => {
          console.timeEnd("多個執行緒計算用時2");
          console.log("err", err);
          this.loadingTwo = false;
        });
    },
    // 呼叫方式三 根據message的名字去指定誰(可多個)去執行 物件形式
    byMessageNameObjParamsFn() {
      this.loadingThree = true;
      console.time("多個執行緒計算用時3");
      this.worker2
        .postAll([{ message: "fn1", args: ["程式碼修仙路漫漫", "加油幹"] }]) // 這裡指定"fn1" 去執行
        .then((res) => {
          console.timeEnd("多個執行緒計算用時3");
          console.log("res", res); // 結果是一個陣列 []
          this.loadingThree = false;
        })
        .catch((err) => {
          console.timeEnd("多個執行緒計算用時3");
          console.log("err", err);
          this.loadingThree = false;
        });
    },
    /**
     * 使用Promise
     * */
    async usePromiseFn() {
      function asyncOne() {
        let async1 = new Promise(async (resolve, reject) => {
          function fib(n) {
            if ((n === 1) | (n === 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 1);
            }
          }
          resolve(fib(30));
        });
        return async1;
      }

      function asyncTwo() {
        let async2 = new Promise(async (resolve, reject) => {
          function fib(n) {
            if ((n === 1) | (n === 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 1);
            }
          }
          resolve(fib(30));
        });
        return async2;
      }

      function asyncThree() {
        let async3 = new Promise(async (resolve, reject) => {
          function fib(n) {
            if ((n === 1) | (n === 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 1);
            }
          }
          resolve(fib(30));
        });
        return async3;
      }

      console.time("使用Promise搭配aysnc和await");

      // let paramsArr = [asyncOne()]; // 計算一個大概在3秒左右(計算一次重新整理一次頁面,精確一些)
      let paramsArr = [asyncOne(), asyncTwo(), asyncThree()]; // 計算三個耗時任務大概在9秒左右

      let res = await Promise.all(paramsArr);
      console.timeEnd("使用Promise搭配aysnc和await");
      console.log("結果", res);
    },
  },
  created() {
    // 1. 定義執行緒所用的訊息函式陣列
    const actions = [
      {
        message: "fn1", // message訊息與func函式執行為對映關係
        func: (params1, params2) => {
          console.log("params引數-->", params1, params2);
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          return fib(30);
        },
      },
      {
        message: "fn2",
        func: () => {
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          return fib(30);
        },
      },
      {
        message: "fn3",
        func: () => {
          function fib(n) {
            if ((n == 1) | (n == 2)) {
              return 1;
            } else {
              return fib(n - 1) + fib(n - 2);
            }
          }
          // throw "一個報錯掛了,其他的也跟著掛了,走.catch"; // 丟擲錯誤(的確很像Promise.all())
          return fib(30);
        },
      },
    ];
    // 2. 根據訊息函式陣列去create建立一個worker,並存到data變數中去,以便於後續隨時使用
    this.worker2 = this.$worker.create(actions);
  },
  // 別忘了在元件銷燬前清除掉哦
  beforeDestroy() {
    this.worker = null;
  },
};
</script>

<style lang='less' scoped>
.bold {
  font-weight: 700;
  font-size: 24px;
}
.info {
  color: #999;
  font-size: 13px;
}
.UI {
  display: flex;
  align-items: center;
}
</style>

或者大家去筆者的GitHub倉庫中拉取程式碼,跑起來看,更加方便理解。

GitHub倉庫地址:https://github.com/shuirongsh...

js中統計程式碼執行時長的三種方式

附帶js中常用的統計程式執行時長的三種方式

// 計算時間方式一 
function fib(n) {
        if (n === 1 | n === 2) {
                return 1
        } else {
                return fib(n - 1) + fib(n - 2)
        }
}
let start = window.performance.now() // 單位毫秒
fib(40)
let end = window.performance.now() // 單位毫秒
console.log((end - start).toFixed(0) + '毫秒');
// 計算時間方式二 
function fib(n) {
        if (n === 1 | n === 2) {
                return 1
        } else {
                return fib(n - 1) + fib(n - 2)
        }
}
let start = Date.now() // 單位毫秒
fib(40)
let end = Date.now() // 單位毫秒
console.log((end - start).toFixed(0) + '毫秒');
console.time('tom')
console.timeEnd('tom')

A good memory is not as good as a bad pen, record it...

相關文章