瀏覽器說:雖然都叫event loop,但是我和node不一樣,eventnode

討論event loop要做到以下兩點(diǎn)

  • 首先要確定好上下文,nodejs和瀏覽器的event loop是兩個(gè)有明確區(qū)分的事物,不能混為一談。愛掏網(wǎng) - it200.com
  • 其次,討論一些js異步代碼的執(zhí)行順序時(shí)候,要基于node的源碼而不是自己的臆想。愛掏網(wǎng) - it200.com

簡單來講:

  • nodejs的event是基于libuv,而瀏覽器的event loop則在html5的規(guī)范中明確定義。愛掏網(wǎng) - it200.com
  • libuv已經(jīng)對(duì)event loop作出了實(shí)現(xiàn),而html5規(guī)范中只是定義了瀏覽器中event loop的模型,具體實(shí)現(xiàn)留給了瀏覽器廠商。愛掏網(wǎng) - it200.com

瀏覽器中的event loop


4c060daf27fd5830ae3ad96ac1b9aa13bd3ab770

瀏覽器事件環(huán)中js分為兩部分,一個(gè)叫heap(堆),一個(gè)叫stack(棧)。愛掏網(wǎng) - it200.com

對(duì)象放在heap(堆)里,常見的基礎(chǔ)類型和函數(shù)放在stack(棧)里,函數(shù)執(zhí)行的時(shí)候在棧里執(zhí)行。愛掏網(wǎng) - it200.com棧里函數(shù)執(zhí)行的時(shí)候可能會(huì)調(diào)一些Dom操作,ajax操作和setTimeout定時(shí)器,這時(shí)候要等stack(棧)里面的所有程序先走(注意:棧里的代碼是先進(jìn)后出),走完后再走WebAPIs,WebAPIs執(zhí)行后的結(jié)果放在callback queue(回調(diào)的隊(duì)列里,注意:隊(duì)列里的代碼先放進(jìn)去的先執(zhí)行),也就是當(dāng)棧里面的程序走完之后,再從任務(wù)隊(duì)列中讀取事件,將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行,這個(gè)過程是循環(huán)不斷的。愛掏網(wǎng) - it200.com

簡單來講:

  • 1.所有同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧
  • 2.主線程之外,還存在一個(gè)任務(wù)隊(duì)列。愛掏網(wǎng) - it200.com只要異步任務(wù)有了運(yùn)行結(jié)果,就在任務(wù)隊(duì)列之中放置一個(gè)事件。愛掏網(wǎng) - it200.com
  • 3.一旦執(zhí)行棧中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取任務(wù)隊(duì)列,將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行
  • 4.主線程從任務(wù)隊(duì)列中讀取事件,這個(gè)過程是循環(huán)不斷的

整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))

概念中首先要明白是:stack(棧)和queue(隊(duì)列)的區(qū)別,它們是怎么去執(zhí)行的?

棧方法LIFO(Last In First Out):先進(jìn)后出(先進(jìn)的后出),典型的就是函數(shù)調(diào)用。愛掏網(wǎng) - it200.com



//執(zhí)行上下文棧  作用域
var a = "aa";
function one(){
    let a = 1;
    two();
    function two(){
        let b = 2;
        three();
        function three(){
            console.log(b)
        }
    }
}
console.log(a);
one();


aa
2

圖解執(zhí)行原理:

a478f44ac65d3a40b3ff4ecdd66a2d84e0fa1f8f

執(zhí)行棧里面最先放的是全局作用域(代碼執(zhí)行有一個(gè)全局文本的環(huán)境),然后再放one, one執(zhí)行再把two放進(jìn)來,two執(zhí)行再把three放進(jìn)來,一層疊一層。愛掏網(wǎng) - it200.com

那么怎么出呢,怎么銷毀的呢?

最先走的肯定是three,因?yàn)閠wo要是先銷毀了,那three的代碼b就拿不到了,所以是先進(jìn)后出(先進(jìn)的后出),所以,three最先出,然后是two出,再是one出。愛掏網(wǎng) - it200.com

隊(duì)列方法FIFO(First In First Out)

(隊(duì)頭)[1,2,3,4](隊(duì)尾) 進(jìn)的時(shí)候從隊(duì)尾依次進(jìn)1,2,3,4 出的時(shí)候從對(duì)頭依次出1,2,3,4

ccc37d41c5144bf6d0bd2b987f677641a768cf70

瀏覽器事件環(huán)中代碼執(zhí)行都是按棧的結(jié)果去執(zhí)行的,但是我們調(diào)用完多線程的方法(WebAPIs),這些多線程的方法是放在隊(duì)列里的,也就是先放到隊(duì)列里的方法先執(zhí)行。愛掏網(wǎng) - it200.com

那什么時(shí)候WebAPIs里的方法會(huì)再執(zhí)行呢?

比如:stack(棧)里面都走完之后,就會(huì)依次讀取任務(wù)隊(duì)列,將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行,這個(gè)時(shí)候棧中又出現(xiàn)了事件,這個(gè)事件又去調(diào)用了WebAPIs里的異步方法,那這些異步方法會(huì)在再被調(diào)用的時(shí)候放在隊(duì)列里,然后這個(gè)主線程(也就是stack)執(zhí)行完后又將從任務(wù)隊(duì)列中依次讀取事件,這個(gè)過程是循環(huán)不斷的。愛掏網(wǎng) - it200.com

下面通過列子來說明:

例子1


console.log(1);
console.log(2);
setTimeout(function(){
    console.log(3);
})
setTimeout(function(){
    console.log(4);
})
console.log(5);


// 結(jié)果
1
2
5
3
4

1、首先執(zhí)行棧里面的同步代碼 1

2

5

2、棧里面的setTimeout事件會(huì)依次放到任務(wù)隊(duì)列中,當(dāng)棧里面都執(zhí)行完之后,再依次從從任務(wù)隊(duì)列中讀取事件往棧里面去執(zhí)行。愛掏網(wǎng) - it200.com

3

4

例子2
console.log(1);
console.log(2);

setTimeout(function(){
    console.log(3);
    setTimeout(function(){
        console.log(6);
    })
})
setTimeout(function(){
    console.log(4);
    setTimeout(function(){
        console.log(7);
    })
})
console.log(5)



// 結(jié)果
1
2
5
3
4
6
7

1、首先執(zhí)行棧里面的同步代碼

1

2

5

2、棧里面的setTimeout事件會(huì)依次放到任務(wù)隊(duì)列中,當(dāng)棧里面都執(zhí)行完之后,再依次從從任務(wù)隊(duì)列中讀取事件往棧里面去執(zhí)行。愛掏網(wǎng) - it200.com

3

4

3、當(dāng)執(zhí)行棧開始依次執(zhí)行setTimeout時(shí),會(huì)將setTimeout里面的嵌套setTimeout依次放入隊(duì)列中,然后當(dāng)執(zhí)行棧中的setTimeout執(zhí)行完畢后,再依次從從任務(wù)隊(duì)列中讀取事件往棧里面去執(zhí)行。愛掏網(wǎng) - it200.com

6

7

例子3



console.log(1);
console.log(2);

setTimeout(function(){
    console.log(3);
    setTimeout(function(){
        console.log(6);
    })
},400)
setTimeout(function(){
    console.log(4);
    setTimeout(function(){
        console.log(7);
    })
},100)
console.log(5)


// 結(jié)果
1
2
5
4
7
3
6

在例子2的基礎(chǔ)上,如果設(shè)置了setTimeout的時(shí)間,那就是按setTimeout的成功時(shí)間依次執(zhí)行。愛掏網(wǎng) - it200.com

如上:這里的順序是1,2,5,4,7,3,6。愛掏網(wǎng) - it200.com也就是只要兩個(gè)set時(shí)間不一樣的時(shí)候 ,就set時(shí)間短的先走完,包括set里面的回調(diào)函數(shù),再走set時(shí)間慢的。愛掏網(wǎng) - it200.com(因?yàn)橹挥挟?dāng)時(shí)間到了的時(shí)候,才會(huì)把set放到隊(duì)列里面去,這一點(diǎn)跟nodejs中的set設(shè)置了時(shí)間的機(jī)制差不多,可以看nodejs中的例子6,也是會(huì)先走完時(shí)間短,再走時(shí)間慢的。愛掏網(wǎng) - it200.com

例子4

當(dāng)觸發(fā)回調(diào)函數(shù)時(shí),會(huì)將回調(diào)函數(shù)放到隊(duì)列中。愛掏網(wǎng) - it200.com永遠(yuǎn)都是棧里面執(zhí)行完后再從任務(wù)隊(duì)列中讀取事件往棧里面去執(zhí)行。愛掏網(wǎng) - it200.com



setTimeout(function(){
    console.log('setTimeout')
},4)
for(var i = 0;i


// 結(jié)果
0
1
2
3
4
5
6
7
8
9
setTimeout

在學(xué)習(xí)nodejs事件環(huán)之前,我們先了解一下宏任務(wù)和微任務(wù)在瀏覽器中的執(zhí)行機(jī)制。愛掏網(wǎng) - it200.com也是面試中經(jīng)常會(huì)被問到的。愛掏網(wǎng) - it200.com

宏任務(wù)和微任務(wù)

83956e44693106421ce7be28b7a1c40ff6d074ea

任務(wù)可分為宏任務(wù)和微任務(wù),宏任務(wù)和微任務(wù)都是隊(duì)列

  • macro-task(宏任務(wù)): setTimeout, setInterval, setImmediate, I/O
  • micro-task(微任務(wù)): process.nextTick, 原生Promise(有些實(shí)現(xiàn)的promise將then方法放到了宏任務(wù)中),Object.observe(已廢棄), MutationObserver不兼容的,MessageChannel(消息通道,類似worker)

Promise.then(源碼見到Promise就用setTimeout),then方法不應(yīng)該放到宏任務(wù)中(源碼中寫setTimeout是迫不得已的),默認(rèn)瀏覽器的實(shí)現(xiàn)這個(gè)then放到了微任務(wù)中。愛掏網(wǎng) - it200.com例如:



console.log(1)
let promise = new Promise(function(resolve,reject){
    console.log(3)
    resolve(100)
}).then(function(data){
    console.log(100)
})
console.log(2)


1
3
2
100

先走console.log(1),這里的new Promise()是立即執(zhí)行的,所以是同步的,由于這個(gè)then在console.log(2)后面執(zhí)行的,所以不是同步,是異步的。愛掏網(wǎng) - it200.com

那這跟宏任務(wù)和微任務(wù)有什么關(guān)系?

我們可以加一個(gè)setTimeout(宏任務(wù))對(duì)比一下:



console.log(1)
setTimeout(function(){
    console.log('setTimeout')
},0)
let promise = new Promise(function(resolve,reject){
    console.log(3)
    resolve(100)
}).then(function(data){
    console.log(100)
})
console.log(2)


1
3
2
100
setTimeout

結(jié)論:在瀏覽器事件環(huán)機(jī)制中,同步代碼先執(zhí)行 執(zhí)行是在棧中執(zhí)行的,然后微任務(wù)會(huì)先執(zhí)行,再執(zhí)行宏任務(wù)

MutationObserver例子:





    


Document

    
    




// 結(jié)果
1
2
渲染完成

MessageChannel例子

vue中nextTick的實(shí)現(xiàn)原理就是通過這個(gè)方法實(shí)現(xiàn)的


console.log(1);
let channel = new MessageChannel();
let port1 = channel.port1;
let port2 = channel.port2;
port1.onmessage = function(e){
    console.log(e.data);
}
console.log(2);
port2.postMessage(100);
console.log(3)


// 瀏覽器中console結(jié)果 會(huì)等所有同步代碼執(zhí)行完再執(zhí)行,所以是微任務(wù)晚于同步的
1
2
3
100

nodejs中的event loop

node的特點(diǎn):異步 非阻塞i/o node通過LIBUV這個(gè)庫自己實(shí)現(xiàn)的異步,默認(rèn)的情況下是沒有異步的方法的。愛掏網(wǎng) - it200.com

nodejs中的event loop有6個(gè)階段,這里我們重點(diǎn)關(guān)注poll階段(fs的i/o操作,對(duì)文件的操作,i/o里面的回調(diào)函數(shù)都放在這個(gè)階段)

4ad4921e92f7fcd1fecd82b7620b0f5f137e0ea3
event loop的每一次循環(huán)都需要依次經(jīng)過上述的階段。愛掏網(wǎng) - it200.com 每個(gè)階段都有自己的callback隊(duì)列,每當(dāng)進(jìn)入某個(gè)階段,都會(huì)從所屬的隊(duì)列中取出callback來執(zhí)行,當(dāng)隊(duì)列為空或者被執(zhí)行callback的數(shù)量達(dá)到系統(tǒng)的最大數(shù)量時(shí),進(jìn)入下一階段。愛掏網(wǎng) - it200.com這六個(gè)階段都執(zhí)行完畢稱為一輪循環(huán)。愛掏網(wǎng) - it200.com

下面通過列子來說明:

例子1


console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise')
    })
})
setTimeout(function(){
    console.log('setTimeout2')
})


-> node eventloop.js
1
2
setTimeout1
setTimeout2
Promise

圖解執(zhí)行原理: 47196a4af2ad2b9118bf71ebcbb359729297bee7

1、首先執(zhí)行完棧里面的代碼

console.log(1);

console.log(2);

2、從棧進(jìn)入到event loop的timers階段,由于nodejs的event loop是每個(gè)階段的callback執(zhí)行完畢后才會(huì)進(jìn)入下一個(gè)階段,所以會(huì)打印出timers階段的兩個(gè)setTimeout的回調(diào)

setTimeout1

setTimeout2

3、由于node event中微任務(wù)不在event loop的任何階段執(zhí)行,而是在各個(gè)階段切換的中間執(zhí)行,即從一個(gè)階段切換到下個(gè)階段前執(zhí)行。愛掏網(wǎng) - it200.com所以當(dāng)times階段的callback執(zhí)行完畢,準(zhǔn)備切換到下一個(gè)階段時(shí),執(zhí)行微任務(wù)(打印出Piromise),

Promise

如果例子1看懂了,以下例子2-例子6自己走一遍。愛掏網(wǎng) - it200.com需要注意的是例子6,當(dāng)setTimeout設(shè)置了時(shí)間,優(yōu)先按時(shí)間順序執(zhí)行(瀏覽器事件環(huán)中例子3差不多)。愛掏網(wǎng) - it200.com例子7,例子8是重點(diǎn)。愛掏網(wǎng) - it200.com

例子2



console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise')
    })
},1000)
setTimeout(function(){
    console.log('setTimeout2')
},1000)


-> node eventloop.js
1
2
setTimeout1
setTimeout2
Promise

例子3


console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise')
    })
},2000)
setTimeout(function(){
    console.log('setTimeout2')
},1000)


-> node eventloop.js
1
2
setTimeout2
setTimeout1
Promise

例子4
console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise1')
    })
})
setTimeout(function(){
    console.log('setTimeout2')
    Promise.resolve().then(function(){
        console.log('Promise2')
    })
})


-> node eventloop.js
1
2
setTimeout1
setTimeout2
Promise1
Promise2

例子5



console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise1')
    })
},1000)
setTimeout(function(){
    console.log('setTimeout2')
    Promise.resolve().then(function(){
        console.log('Promise2')
    })
},1000)


-> node eventloop.js
1
2
setTimeout1
setTimeout2
Promise1
Promise2

例子6

console.log(1);
console.log(2);
setTimeout(function(){
    console.log('setTimeout1')
    Promise.resolve().then(function(){
        console.log('Promise1')
    })
},2000)
setTimeout(function(){
    console.log('setTimeout2')
    Promise.resolve().then(function(){
        console.log('Promise2')
    })
},1000)


-> node eventloop.js
1
2
setTimeout2
Promise2
setTimeout1
Promise1

例子7:setImmediate() vs setTimeout()

  • setImmediate 設(shè)計(jì)在poll階段完成時(shí)執(zhí)行,即check階段;
  • setTimeout 設(shè)計(jì)在poll階段為空閑時(shí),且設(shè)定時(shí)間到達(dá)后執(zhí)行;但其在timer階段執(zhí)行

其二者的調(diào)用順序取決于當(dāng)前event loop的上下文,如果他們?cè)诋惒絠/o callback之外調(diào)用,其執(zhí)行先后順序是不確定的


setTimeout(function timeout () {
  console.log('timeout');
},0);

setImmediate(function immediate () {
  console.log('immediate');
});


-> node eventloop.js
timeout
immediate

-> node eventloop.js
immediate
timeout

但當(dāng)二者在異步i/o callback內(nèi)部調(diào)用時(shí),總是先執(zhí)行setImmediate,再執(zhí)行setTimeout

這是因?yàn)閒s.readFile callback執(zhí)行完后,程序設(shè)定了timer 和 setImmediate,因此poll階段不會(huì)被阻塞進(jìn)而進(jìn)入check階段先執(zhí)行setImmediate,后進(jìn)入timer階段執(zhí)行setTimeout


var fs = require('fs')

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('timeout')
  }, 0)
  setImmediate(() => {
    console.log('immediate')
  })
})


$ node eventloop.js
immediate
timeout

例子8:process.nextTick()

process.nextTick()不在event loop的任何階段執(zhí)行,而是在各個(gè)階段切換的中間執(zhí)行,即從一個(gè)階段切換到下個(gè)階段前執(zhí)行。愛掏網(wǎng) - it200.com


function Fn(){
    this.arrs;
    process.nextTick(()=>{
        this.arrs();
    })
}
Fn.prototype.then = function(){
    this.arrs = function(){console.log(1)}
}
let fn = new Fn();
fn.then();


-> node eventloop.js
   1

不加process.nextTick,new Fn()的時(shí)候,this.arrs是undefind,this.arrs()執(zhí)行會(huì)報(bào)錯(cuò);

加了process.nextTick,new Fn()的時(shí)候,this.arrs()不會(huì)執(zhí)行(因?yàn)閜rocess.nextTick是微任務(wù),只有在各個(gè)階段切換的中間執(zhí)行,所以它會(huì)等到同步代碼執(zhí)行完之后才會(huì)執(zhí)行)這個(gè)時(shí)候同步代碼fn.then()執(zhí)行=>this.arrs = function(){console.log(1)},this.arrs變成了一個(gè)函數(shù),同步執(zhí)行完后再去執(zhí)行process.nextTick(()=>{this.arrs();})就不會(huì)報(bào)錯(cuò)。愛掏網(wǎng) - it200.com

需要注意的是:nextTick千萬不要寫遞歸,可以放一些比setTimeout優(yōu)先執(zhí)行的任務(wù)


// 死循環(huán),會(huì)一直執(zhí)行微任務(wù),卡機(jī)
function nextTick(){
    process.nextTick(function(){
        nextTick();
    })
}
nextTick()
setTimeout(function(){

},499)

最后再來段代碼加深理解


var fs = require('fs');

fs.readFile(__filename, () => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    console.log('setImmediate');
    process.nextTick(()=>{
      console.log('nextTick3');
    })
  });
  process.nextTick(()=>{
    console.log('nextTick1');
  })
  process.nextTick(()=>{
    console.log('nextTick2');
  })
});


-> node eventloop.js
nextTick1
nextTick2
setImmediate
nextTick3
setTimeout

1、從poll —> check階段,先執(zhí)行process.nextTick,

nextTick1

nextTick2

2、然后進(jìn)入check,setImmediate,

setImmediate

3、執(zhí)行完setImmediate后,出check,進(jìn)入close callback前,執(zhí)行process.nextTick

nextTick3

4、最后進(jìn)入timer執(zhí)行setTimeout

setTimeout

結(jié)論:在nodejs事件環(huán)機(jī)制中,微任務(wù)是在各個(gè)階段切換的中間去執(zhí)行的。愛掏網(wǎng) - it200.com

最后

  • 在瀏覽器的事件環(huán)機(jī)制中,我們需要了解的是棧和隊(duì)列是怎么去執(zhí)行的。愛掏網(wǎng) - it200.com

    棧:先進(jìn)后出;隊(duì)列:先進(jìn)先出。愛掏網(wǎng) - it200.com

    所有代碼在棧中執(zhí)行,棧中的DOM,ajax,setTimeout會(huì)依次進(jìn)入到隊(duì)列中,當(dāng)棧中代碼執(zhí)行完畢后,有微任務(wù)先會(huì)將微任務(wù)依次從隊(duì)列中取出放到執(zhí)行棧中執(zhí)行,最后再依次將隊(duì)列中的事件放到執(zhí)行棧中依次執(zhí)行。愛掏網(wǎng) - it200.com

  • 在nodejs的事件環(huán)機(jī)制中,我們需要了解的是node的執(zhí)行機(jī)制是階段型的,微任務(wù)不屬于任何階段,而是在各個(gè)階段切換的中間執(zhí)行。愛掏網(wǎng) - it200.comnodejs把事件環(huán)分成了6階段,這里需要注意的是,當(dāng)執(zhí)行棧里的同步代碼執(zhí)行完畢切換到node的event loop時(shí)也屬于階段切換,這時(shí)候也會(huì)先去清空微任務(wù)。愛掏網(wǎng) - it200.com

  • 微任務(wù)和宏任務(wù)

    macro-task(宏任務(wù)): setTimeout, setInterval, setImmediate, I/O

    micro-task(微任務(wù)): process.nextTick, 原生Promise(有些實(shí)現(xiàn)的promise將then方法放到了宏任務(wù)中),Object.observe(已廢棄), MutationObserver不兼容的

問題

如果在執(zhí)行宏任務(wù)的過程中又發(fā)現(xiàn)了回調(diào)中有微任務(wù),會(huì)把這個(gè)微任務(wù)提前到所有宏任務(wù)之前,等到這個(gè)微任務(wù)完成后再繼續(xù)執(zhí)行宏任務(wù)嗎?


console.log(1);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log('promise1')
    })
})
setTimeout(function(){
    console.log(3)
    Promise.resolve(1).then(function(){
        console.log('promise2')
    })
})
setTimeout(function(){
    console.log(4)
    Promise.resolve(1).then(function(){
        console.log('promise3')
    })
})


// node中 每個(gè)階段切換中間執(zhí)行微任務(wù)
1
2
3
4
promise1
promise2
promise3


// 瀏覽器中  先走微任務(wù)
1
VM59:3 2
VM59:5 promise1
VM59:9 3
VM59:11 promise2
VM59:15 4
VM59:17 promise3

以下例子也可以看看
// 例子1
console.log(1);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log('promise1')

        setTimeout(function(){
            console.log(3)
            Promise.resolve(1).then(function(){
                console.log('promise2')
            })
        })

    })
})


// node
1
2
promise1
3
promise2


// 瀏覽器
1
VM70:3 2
VM70:5 promise1
VM70:8 3
VM70:10 promise2


// 例子2
console.log(11);
setTimeout(function(){
    console.log(2);
    Promise.resolve(1).then(function(){
        console.log('promise1')
    })
    setTimeout(function(){
        console.log(3)
        Promise.resolve(1).then(function(){
            console.log('promise2')
        })
    })
})


// node
11
2
promise1
3
promise2


// 瀏覽器
11
VM73:4 2
VM73:6 promise1
VM73:9 3
VM73:11 promise2


原文發(fā)布時(shí)間為:2024年06月04日
原文作者:我是家碧

本文來源:?掘金?如需轉(zhuǎn)載請(qǐng)聯(lián)系原作者



聲明:所有內(nèi)容來自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
發(fā)表評(píng)論
更多 網(wǎng)友評(píng)論0 條評(píng)論)
暫無評(píng)論

返回頂部

主站蜘蛛池模板: 国产jizzjizz免费视频| 日韩精品无码人成视频手机| 边吃奶边摸下我好爽免费视频 | 高嫁肉柳风车动漫| 特黄熟妇丰满人妻无码| 日本中文字幕黑人借宿影片| 在线视频免费观看a毛片| 国产一卡2卡3卡4卡无卡免费视频 国产一卡2卡3卡4卡网站免费 | 国产大陆xxxx做受视频| 亚洲色婷婷一区二区三区| 一本无码中文字幕在线观| 精品视频www| 日本精品一卡二卡≡卡四卡| 国内精品久久久久伊人av| 啊~嗯~轻点~啊~用力村妇 | 国产成人久久精品一区二区三区| 亚洲精品NV久久久久久久久久| 中文有码在线观看| 天天影视色香欲性综合网网站| 欧美大陆日韩一区二区三区| 天天天操天天天干| 厨房切底征服岳| 久久亚洲成a人片| 奇米影视国产精品四色| 激情综合色综合久久综合| 岳一夜要我六次| 免费a级片在线观看| 三级黄色在线看| 看全色黄大色大片免费久久| 成人免费乱码大片a毛片| 免费国产污网站在线观看| 丁香六月色婷婷| 琪琪色原网站在线观看| 女人又黄的视频网站| 亚洲欧美日韩国产精品| www.日日夜夜| 精品无码人妻一区二区三区品 | 欧美精品在线观看| 在线观看免费a∨网站| 亚洲黄色a级片| 中国人xxxxx69免费视频|