JavaScript 系列--JavaScript一些奇淫技巧的實現方法(一)簡短的sleep函式,獲取時間戳

saucxs發表於2019-05-30

一、前言

有些東西很好用,但是你未必知道;有些東西你可能用過,但是你未必知道原理。
實現一個目的有多種途徑,俗話說,條條大路通羅馬。發散一下大家的思維以及擴充一下知識面。

 

二、實現一個簡短的sleep函式

sleep函式主要用來做延遲執行的,很多程式語言都有sleep函式,但是javascript沒有這個函式,我們實現一下:

1、簡單版本

function sleep(sleepTime){
    for(var start = +new Date;+new Date - start<sleepTime;){}
}
var t1 =  +new Date();
sleep(3000);
var t2 = +new Date();
console.log(t2-t1);

優點:簡單粗暴,通俗易懂。

缺點:確實sleep了,但是卡死了,cpu會飆升,精確度不準

 

2、promise版本

// promise版本
function sleep(sleepTime){
    return new Promise(resolve => setTimeout(resolve,sleepTime));
}
var t1 = +new Date();
sleep(3000).then(()=>{
    var t2 = +new Date();
    console.log(t2-t1);
})

優點:實際上用了setTimeout,沒有形成程式阻塞,不會造成效能和負載問題。

缺點:雖然解決了回撥函式的巢狀,但是還是不美觀,而且非同步不徹底,過程中停止執行。

 

3、generator版本

// generotor版本
function sleep(sleepTime){
    return function(cb){
        setTimeout(cb.bind(this), sleepTime);
    }
}
function* genSleep(){
    var t1 = +new Date();
    yield sleep(3000);
    var t2 = +new Date();
    console.log(t2-t1);
}
async(genSleep);
function async(gen){
    const iter = gen();
    function nextStep(it){
        if(it.done) return ;
        if (typeof it.value === "function") {
            it.value(function(ret) {
              nextStep(iter.next(ret))
            })
        } else {
            nextStep(iter.next(it.value));
        }
    }
    nextStep(iter.next());
}

優點:跟promise一樣優點,程式碼變得更簡單幹淨。

缺點:就是每次都要執行 next() 顯得很麻煩,雖然有 co(第三方包)可以解決,但就多包了一層,不好看,錯誤也必須按 co 的邏輯來處理,不爽。

co 之所以這麼火併不是沒有原因的,當然不是僅僅實現 sleep 這麼無聊的事情,而是它活生生的藉著generator/yield 實現了很類似 async/await 的效果!這一點真是讓我三觀盡毀刮目相看。

const co = require("co")
function sleep(sleepTime) {
  return function(cb) {
    setTimeout(cb.bind(this), sleepTime)
  }
}

co(function*() {
  const t1 = +new Date()
  yield sleep(3000)
  const t2 = +new Date()
  console.log(t2 - t1)
})

 

4、async/await版本

function sleep(delay) {
  return new Promise(reslove => {
    setTimeout(reslove, delay)
  })
}

!async function test() {
  const t1 = +new Date()
  await sleep(3000)
  const t2 = +new Date()
  console.log(t2 - t1)
}()

優點:同 Promise 和 Generator 優點。 Async/Await 可以看做是 Generator 的語法糖,Async 和 Await 相較於 * 和 yield 更加語義,另外各個函式都是扁平的,不會產生多餘的巢狀,程式碼更加清爽易讀。

缺點:ES7 語法存在相容性問題,有 babel 一切相容性都不是問題

 

5、開源的版本

在 javascript 優雅的寫 sleep 等於如何優雅的不優雅,這裡有 C++ 實現的模組:https://github.com/ErikDubbelboer/node-sleep

const sleep = require("sleep")

const t1 = +new Date()
sleep.msleep(3000)
const t2 = +new Date()
console.log(t2 - t1)

優點:能夠實現更加精細的時間精確度,而且看起來就是真的 sleep 函式,清晰直白。
缺點:缺點需要安裝這個模組node-sleep

 

前端知識點Async/Await是目前前端非同步書寫最優雅的一種方式

 

二、優雅獲取時間戳

上面實現 sleep 函式,我們可以發現程式碼有 +new Date()獲取時間戳的用法,這只是其中的一種,下面就說一下其他兩種以及 +new Date()的原理。

1、普通版

var timestamp=new Date().getTime()

優點:具有普遍性,大家都用這個
缺點:應該沒有吧

 

2、進階版

var timestamp = (new Date()).valueOf()

valueOf 方法返回物件的原始值(Primitive,'Null','Undefined','String','Boolean','Number'五種基本資料型別之一),可能是字串、數值或 bool 值等,看具體的物件。

優點:說明開發者原始值有一個具體的認知,讓人眼前一亮。
缺點: 應該沒有吧

 

3、Date.now()方法

Date.now()

Date.now() 方法返回自1970年1月1日 00:00:00 UTC到當前時間的毫秒數。型別為Number。因為 now() 是Date的一個靜態函式,所以必須以 Date.now() 的形式來使用。

優點:簡單明瞭。

缺點:相容性問題,ECMA-262 第五版中被標準化。

相容性不支援時的相容性程式碼:

if (!Date.now) {
  Date.now = function now() {
    return new Date().getTime();
  };
}

 

4、終極版

var timestamp = +new Date()

優點:對 JavaScript 隱式轉換掌握的比較牢固的一個表現
缺點:應該沒有吧

我們來分析一下,為什麼+new Date()拿到的時間戳?

那就是隱式轉換,實質上還是呼叫了valueOf()的方法。

 

注意:

(1)一元+ 運算子

一元 + 運算子將其運算元轉換為 Number 型別並反轉其正負。注意負的 +0 產生 -0,負的 -0 產生 +0

+new Date() 相當於 ToNumber(new Date())

 

(2)toString 用來返回物件的字串表示。

var obj = {};
console.log(obj.toString());//[object Object]

var arr = [];
console.log(arr.toString());//""空字串

var date = new Date();        // Tue May 28 2019 22:05:58 GMT+0800 (中國標準時間)
console.log(date.toString());//"Tue May 28 2019 22:05:58 GMT+0800 (中國標準時間)"

 

(3)valueOf()方法返回物件的原始值

valueOf()方法返回物件的原始值,可能是字串,述職或boolean值,看具體的物件。

var obj = {
  name: "saucxs"
}
console.log(obj.valueOf()) //Object {name: "saucxs"}

var arr1 = [1,3]
console.log(arr1.valueOf()) //[1,3]

var date = new Date()
console.log(date.valueOf())//1456638436303
// 如程式碼所示,三個不同的物件例項呼叫valueOf返回不同的資料

原始值指的是 'Null','Undefined','String','Boolean','Number','Symbol' 6種基本資料型別之一,上面已經提到過這個概念,這裡再次申明一下。

 

最後分解一下其中的過程:+new Date():    

(1)運算子 new 的優先順序高於一元運算子 +,所以過程可以分解為:var time=new Date();+time

(2)根據上面提到的規則相當於:ToNumber(time)

(3)time 是個日期物件,根據 ToNumber 的轉換規則,所以相當於:ToNumber(ToPrimitive(time))

(4)根據 ToPrimitive 的轉換規則:ToNumber(time.valueOf()),time.valueOf() 就是 原始值 得到的是個時間戳,假設 time.valueOf()=1503479124652

(5)所以 ToNumber(1503479124652) 返回值是 1503479124652 這個數字。

前端知識點:隱式轉換的妙用

 

相關文章