1. 程式人生 > >[js高手之路]javascript騰訊面試題學習封裝一個簡易的異步隊列

[js高手之路]javascript騰訊面試題學習封裝一個簡易的異步隊列

騰訊 selector host .proto 算法 obj 代碼 ner 試題

這道js的面試題,是這樣的,頁面上有一個按鈕,一個ul,點擊按鈕的時候,每隔1秒鐘向ul的後面追加一個li, 一共追加10個,li的內容從0開始技術( 0, 1, 2, ....9 ),首先我們用閉包封裝一個創建li元素的函數.

1         var create = (function(){
2             var count = 0;
3             return function(){
4                 var oLi = document.createElement( "li" );
5                 oLi.innerHTML = count++;
6 return oLi; 7 } 8 })();

頁面上的2個元素:

<input type="button" value="點我"> <ul id="box"></ul> js代碼:
 1 var oBtn = document.querySelector( "input" );
 2 var oBox = document.querySelector( "#box" );
 3 
 4 var create = (function(){
 5     var count = 0;
6 return function(){ 7 var oLi = document.createElement( "li" ); 8 oLi.innerHTML = count++; 9 return oLi; 10 } 11 })(); 12 13 oBtn.onclick = function(){ 14 setTimeout(function(){ 15 oBox.appendChild( create() ); 16 setTimeout( function(){ 17 oBox.appendChild( create() );
18 setTimeout( function(){ 19 oBox.appendChild( create() ); 20 }, 1000 ); 21 }, 1000 ); 22 }, 1000 ); 23 }

點擊按鈕的時候,用回調函數嵌套方式,這裏我加入3個li,就已經快受不了了,這就是javascript著名的回調地獄,那麽在這裏,我用循環簡化一下:

 1 var oBtn = document.querySelector("input");
 2 var oBox = document.querySelector("#box");
 3 var timer = oNode =  null;
 4 var create = (function () {
 5     var count = 0;
 6     return function () {
 7         var oLi = document.createElement("li");
 8         oLi.innerHTML = count++;
 9         return oLi;
10     }
11 })();
12 function add(){
13     oNode = oBox.appendChild( create() );
14     if ( oNode.innerHTML < 9 ) {
15         timer = setTimeout( add, 1000 );
16     }else {
17         clearTimeout( timer );
18     }
19 }
20 oBtn.onclick = function () {
21     add();
22 }

恩,確實簡化了,但是這種面向過程的方式,耦合性太強,下面呢,我就把這個封裝成一個通用隊列

第一步:封裝一個隊列,包含( 入列,出列),隊列的特點(先進先出,如果你不懂這個,需要去補下基本的數據結構與算法內容)

 1 var Queue = function () {
 2     this.list = []
 3 }
 4 Queue.prototype = {
 5     constructor: Queue,
 6     enQueue: function ( fn ) {
 7         this.list.push( fn );
 8         return this;
 9     },
10     deQueue: function () {
11         var fn = this.list.shift() || function () {};
12         fn.apply( this, arguments );
13     }
14 }

我們來使用它:

 1 var oQ = new Queue();
 2 oQ.enQueue( function(){
 3     console.log( ‘ghostwu1‘ );
 4 }).enQueue( function(){
 5     console.log( ‘ghostwu2‘ );
 6 }).enQueue( function(){
 7     console.log( ‘ghostwu3‘ );
 8 }).deQueue();
 9 while( oQ.list.length ){
10     oQ.deQueue();
11 }

第二步、雖然我們現在實現了一個隊列,但是,這玩意是同步的,接下來繼續改造成異步的:

 1 var oQ = new Queue();
 2 oQ.enQueue( function(){
 3     var _this = this;
 4     console.log( ‘ghostwu1‘ );
 5     setTimeout( function(){ _this.deQueue(); }, 1000 );
 6 }).enQueue( function(){
 7     var _this = this;
 8     console.log( ‘ghostwu2‘ );
 9     setTimeout( function(){ _this.deQueue(); }, 1000 );
10 }).enQueue( function(){
11     var _this = this;
12     console.log( ‘ghostwu3‘ );
13     setTimeout( function(){ _this.deQueue(); }, 1000 );
14 }).deQueue();

第三步、這樣就實現了一個異步隊列, 這裏有個小東西要註意下,把this保存下來,因為定時器的this指向的是window.另外在封裝deQueue(出列)函數時,一定要給個空函數,否則出列完了之後,會報錯,但是這玩意還是有耦合性,繼續改造:

 1 <input type="button" value="點我">
 2 <ul id="box"></ul>
 3 <script>
 4 var Utils = {
 5     isFunction: function (a) {
 6         return Object.prototype.toString.call(a) === ‘[object Function]‘;
 7     },
 8     isNumber: function (a) {
 9         return typeof a === ‘number‘;
10     }
11 };
12 var Queue = function () {
13     this.list = []
14 }
15 Queue.prototype = {
16     constructor: Queue,
17     enQueue: function (fn) {
18         this.list.push(fn);
19         return this;
20     },
21     delay: function (time) {
22         this.list.push(time);
23         return this;
24     },
25     deQueue: function () {
26         var _this = this;
27         var cur = this.list.shift() || function () { };
28         if (Utils.isFunction(cur)) {
29             cur.apply(_this, arguments);
30             if (_this.list.length) _this.deQueue();
31         } else if (Utils.isNumber(cur)) {
32             setTimeout(function () {
33                 _this.deQueue();
34             }, cur);
35         }
36     }
37 }
38 
39 var oBtn = document.querySelector("input");
40 var oBox = document.querySelector("#box");
41 var create = (function () {
42     var count = 0;
43     return function () {
44         var oLi = document.createElement("li");
45         oLi.innerHTML = count++;
46         return oLi;
47     }
48 })();
49 oBtn.onclick = function () {
50     var oQ = new Queue();
51     function add() {
52         for (var i = 0; i < 10; i++) {
53             oQ.enQueue(function () {
54                 oBox.appendChild(create());
55             }).delay(1000);
56         }
57     }
58     add();
59     oQ.deQueue();
60 }
61 </script>

這樣封裝之後,我們的異步隊列就變得通用一點了,把延時和業務邏輯分開處理

[js高手之路]javascript騰訊面試題學習封裝一個簡易的異步隊列