Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
avaScript 是一門單線程執(zhí)行的編程語言。也就是說,同一時(shí)間只能做一件事情。如果前一個(gè)任務(wù)非常耗時(shí),則后續(xù)的任務(wù)就不得不一直等待,從而導(dǎo)致程序假死的問題。
所以為了防止某個(gè)耗時(shí)任務(wù)導(dǎo)致程序假死的問題,JavaScript 把待執(zhí)行的任務(wù)分為了兩類:
1. 同步任務(wù)(synchronous)
又叫做非耗時(shí)任務(wù),指的是在主線程上排隊(duì)執(zhí)行的那些任務(wù),只有前一個(gè)任務(wù)執(zhí)行完畢,才能執(zhí)行后一個(gè)任務(wù)。
2. 異步任務(wù)(asynchronous)
又叫做耗時(shí)任務(wù),異步任務(wù)由JavaScript 委托給宿主環(huán)境進(jìn)行執(zhí)行,當(dāng)異步任務(wù)執(zhí)行完成后,會(huì)通知JavaScript 主線程執(zhí)行異步任務(wù)的回調(diào)函數(shù)。
同步任務(wù)和異步任務(wù)的執(zhí)行過程:
①同步任務(wù)由JavaScript 主線程次序執(zhí)行。
②異步任務(wù)委托給宿主環(huán)境執(zhí)行。
③已完成的異步任務(wù)對(duì)應(yīng)的回調(diào)函數(shù),會(huì)被加入到任務(wù)隊(duì)列中等待執(zhí)行。
④JavaScript 主線程的執(zhí)行棧被清空后,會(huì)讀取任務(wù)隊(duì)列中的回調(diào)函數(shù),次序執(zhí)行。
⑤JavaScript 主線程不斷重復(fù)上面的第4 步。
JavaScript 主線程從“任務(wù)隊(duì)列”中讀取異步任務(wù)的回調(diào)函數(shù),放到執(zhí)行棧中依次執(zhí)行。這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為EventLoop(事件循環(huán))。
import thenFs from ' then-fs '
console
log('A')
thenFs. readFile(' ./files/1.txt', 'utf8' ). then(dataStr => {
console.log('B')
})
setTimeout(( ) => {
console. log('C' )
},0)
console.log( 'D')
正確的輸出結(jié)果:ADCB。
其中:
A 和D 屬于同步任務(wù)。會(huì)根據(jù)代碼的先后順序依次被執(zhí)行。
C 和B 屬于異步任務(wù)。它們的回調(diào)函數(shù)會(huì)被加入到任務(wù)隊(duì)列中,等待主線程空閑時(shí)再執(zhí)行。
為前端人員要回答這個(gè)問題,需要了解這三個(gè)知識(shí)點(diǎn):
首先,js 是單線程的,所謂單線程,通俗的講就是,執(zhí)行代碼是一行一行的往下走(即所謂的同步)
js的核心就是單線程,新出的一些標(biāo)準(zhǔn)(web worker、atomics、SharedArrayBuffer等),允許JavaScript腳本創(chuàng)建多個(gè)線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個(gè)新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì),類比一下node也是單線程,在準(zhǔn)確的說是單線程異步非阻塞模式
上面僅僅是一個(gè) for 循環(huán),而在實(shí)際應(yīng)用中,會(huì)有大量的網(wǎng)絡(luò)請(qǐng)求,它的響應(yīng)時(shí)間是不確定的,這種情況下也要一直等么?顯然是不行的,因而 js 設(shè)計(jì)了異步,即 發(fā)起網(wǎng)絡(luò)請(qǐng)求(諸如 IO 操作,定時(shí)器),由于需要等服務(wù)器響應(yīng),就先不理會(huì),而是去做其他的事兒,等請(qǐng)求返回了結(jié)果的時(shí)候再說(即異步)。
那么如何實(shí)現(xiàn)異步呢?其實(shí)我們平時(shí)已經(jīng)在大量使用了,那就是 callback,例如:
$.ajax({
url: 'http://xxx',
success: function(res) {
console.log(res);
},
});
success 作為函數(shù)傳遞過去并不會(huì)立即執(zhí)行,而是等請(qǐng)求成功了才執(zhí)行,即回調(diào)函數(shù)callback
const fs = require('fs'); fs.rename('舊文件.txt', '新文件.txt', err => { if (err) throw err; console.log('重命名完成'); });
和網(wǎng)絡(luò)請(qǐng)求類似,等到 IO 操作有了結(jié)果(無論成功與否)才會(huì)執(zhí)行第三個(gè)參數(shù):(err)=>{}
從上面我們就可以看出,實(shí)現(xiàn)異步的核心就是回調(diào)鉤子,將 cb 作為參數(shù)傳遞給異步執(zhí)行函數(shù),當(dāng)有了結(jié)果后在觸發(fā) cb。想了解更多,去看看 event-loop 機(jī)制吧。
至于 async/await 是如何出現(xiàn)的呢,在 es6 之前,大多 js 數(shù)項(xiàng)目中會(huì)有類似這樣的代碼:
ajax1(url, () => { ajax2(url, () => { ajax3(url, () => { // do something }); }); });
這種函數(shù)嵌套,大量的回調(diào)函數(shù),使代碼閱讀起來晦澀難懂,不直觀,形象的稱之為回調(diào)地獄(callback hell),所以為了在寫法上能更通俗一點(diǎn),es6+陸續(xù)出現(xiàn)了 Promise、Generator、Async/await,力求在寫法上簡潔明了,可讀性強(qiáng)。
async/await 是參照 Generator 封裝的一套異步處理方案,可以理解為 Generator 的語法糖,
所以了解 async/await 就不得不講一講 Generator,
而 Generator 又依賴于迭代器Iterator,
所以就得先講一講 Iterator,
而 Iterator 的思想呢又來源于單向鏈表,
終于找到源頭了:單向鏈表
wiki:鏈表(Linked list)是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu),是一種線性表,但是并不會(huì)按線性的順序儲(chǔ)存數(shù)據(jù),而是在每一個(gè)節(jié)點(diǎn)里存到下一個(gè)節(jié)點(diǎn)的指針(Pointer)。由于不必須按順序儲(chǔ)存,鏈表在插入的時(shí)候可以達(dá)到 o(1)的復(fù)雜度,比另一種線性表順序表快得多,但是查找一個(gè)節(jié)點(diǎn)或者訪問特定編號(hào)的節(jié)點(diǎn)則需要 o(n)的時(shí)間,而順序表響應(yīng)的時(shí)間復(fù)雜度分別是 o(logn)和 o(1)。
總結(jié)一下鏈表優(yōu)點(diǎn):
單向鏈表:是鏈表中最簡單的一種,它包含兩個(gè)域,一個(gè)信息域和一個(gè)指針域。這個(gè)鏈接指向列表中的下一個(gè)節(jié)點(diǎn),而最后一個(gè)節(jié)點(diǎn)則指向一個(gè)空值。
一個(gè)單向鏈表包含兩個(gè)值: 當(dāng)前節(jié)點(diǎn)的值和一個(gè)指向下一個(gè)節(jié)點(diǎn)的鏈接
單鏈特點(diǎn):節(jié)點(diǎn)的鏈接方向是單向的;相對(duì)于數(shù)組來說,單鏈表的的隨機(jī)訪問速度較慢,但是單鏈表刪除/添加數(shù)據(jù)的效率很高。
理解 js 原型鏈/作用域鏈的話,理解這個(gè)很容易,他們是相通的。
那么如何設(shè)計(jì)一個(gè)單向鏈表呢?這個(gè)取決于我們需要哪些操作,通常有:
看到這些方法是不是有些許熟悉,當(dāng)你用原生 js 或 jq 時(shí)常會(huì)用上面類似的方法,現(xiàn)在根據(jù)上面列出的方法進(jìn)行實(shí)現(xiàn)一個(gè)單向鏈:
通過以上,我假裝你明白什么是單向鏈表,并且能夠用代碼實(shí)現(xiàn)一個(gè)單向鏈表了,下一步開始說一說迭代器 Iterator
Iterator 翻譯過來就是**迭代器(遍歷器)**讓我們先來看看它的遍歷過程(類似于單向鏈表):
一個(gè)對(duì)象要變成可迭代的,必須實(shí)現(xiàn) @@iterator 方法,即對(duì)象(或它原型鏈上的某個(gè)對(duì)象)必須有一個(gè)名字是 Symbol.iterator 的屬性(原生具有該屬性的有:字符串、數(shù)組、類數(shù)組的對(duì)象、Set 和 Map):
當(dāng)一個(gè)對(duì)象需要被迭代的時(shí)候(比如開始用于一個(gè) for..of 循環(huán)中),它的 @@iterator 方法被調(diào)用并且無參數(shù),然后返回一個(gè)用于在迭代中獲得值的迭代器
迭代器協(xié)議:產(chǎn)生一個(gè)有限或無限序列的值,并且當(dāng)所有的值都已經(jīng)被迭代后,就會(huì)有一個(gè)默認(rèn)的返回值
當(dāng)一個(gè)對(duì)象只有滿足下述條件才會(huì)被認(rèn)為是一個(gè)迭代器:
它實(shí)現(xiàn)了一個(gè) next() 的方法,該方法必須返回一個(gè)對(duì)象,對(duì)象有兩個(gè)必要的屬性:
true:迭代器已經(jīng)超過了可迭代次數(shù)。這種情況下,value 的值可以被省略
false:迭代器可以產(chǎn)生序列中的下一個(gè)值。這等效于沒有指定 done 這個(gè)屬性
根據(jù)上面的規(guī)則,咱們來自定義一個(gè)簡單的迭代器:
我們還可以自定義一個(gè)可迭代對(duì)象:
Generator:生成器對(duì)象是生成器函數(shù)(GeneratorFunction)返回的,它符合可迭代協(xié)議和迭代器協(xié)議,既是迭代器也是可迭代對(duì)象,可以調(diào)用 next 方法,但它不是函數(shù),更不是構(gòu)造函數(shù)
生成器函數(shù)(GeneratorFunction):
function* name([param[, param[, ... param]]]) { statements }
調(diào)用一個(gè)生成器函數(shù)并不會(huì)馬上執(zhí)行它里面的語句,而是返回一個(gè)這個(gè)生成器的迭代器對(duì)象,當(dāng)這個(gè)迭代器的 next() 方法被首次(后續(xù))調(diào)用時(shí),其內(nèi)的語句會(huì)執(zhí)行到第一個(gè)(后續(xù))出現(xiàn) yield 的位置為止(讓執(zhí)行處于暫停狀),yield 后緊跟迭代器要返回的值。或者如果用的是 yield*(多了個(gè)星號(hào)),則表示將執(zhí)行權(quán)移交給另一個(gè)生成器函數(shù)(當(dāng)前生成器暫停執(zhí)行),調(diào)用 next() (再啟動(dòng))方法時(shí),如果傳入了參數(shù),那么這個(gè)參數(shù)會(huì)作為上一條執(zhí)行的 yield 語句的返回值,例如:
看到這里,你可能會(huì)問,Generator 和 callback 有啥關(guān)系,如何處理異步呢?其實(shí)二者沒有任何關(guān)系,我們只是通過一些方式強(qiáng)行的它們產(chǎn)生了關(guān)系,才會(huì)有 Generator 處理異步
我們來總結(jié)一下 Generator 的本質(zhì),暫停,它會(huì)讓程序執(zhí)行到指定位置先暫停(yield),然后再啟動(dòng)(next),再暫停(yield),再啟動(dòng)(next),而這個(gè)暫停就很容易讓它和異步操作產(chǎn)生聯(lián)系,因?yàn)槲覀冊(cè)谔幚懋惒綍r(shí):開始異步處理(網(wǎng)絡(luò)求情、IO 操作),然后暫停一下,等處理完了,再該干嘛干嘛。不過值得注意的是,js 是單線程的(又重復(fù)了三遍),異步還是異步,callback 還是 callback,不會(huì)因?yàn)?Generator 而有任何改變
下面來看看,用 Generator 實(shí)現(xiàn)異步:
// g1: { value: Promise { <pending> }, done: false } // res1: { // "a": 1 // } // { // "a": 1 // } // g2: { value: Promise { <pending> }, done: false } // res2: { // "b": 2 // } // { // "b": 2 // }
以上代碼是 Generator 和 callback 結(jié)合實(shí)現(xiàn)的異步,可以看到,仍然需要手動(dòng)執(zhí)行 .then 層層添加回調(diào),但由于 next() 方法返回對(duì)象 {value: xxx,done: true/false} 所以我們可以簡化它,寫一個(gè)自動(dòng)執(zhí)行器:
說了這么多,怎么還沒有到 async/await,馬上來了(其實(shí)我已經(jīng)漏了一些內(nèi)容沒說:Promise 和 callback 的關(guān)系,thunk 函數(shù),co 庫,感興趣的可以去 google 一下,yuanyifeng 老師講的es6 入門非常棒,我時(shí)不時(shí)的都會(huì)去看一看)
首先,async/await 是 Generator 的語法糖,上面我是分割線下的第一句已經(jīng)講過,先來看一下二者的對(duì)比:
可以看到,async function 代替了 function*,await 代替了 yield,同時(shí)也無需自己手寫一個(gè)自動(dòng)執(zhí)行器 run 了
現(xiàn)在再來看看async/await 的特點(diǎn):
res.then(data => { console.log(data); // done });
啊,終于完了,一個(gè) async-await 連帶出來這么多知識(shí)點(diǎn),以后面試被問到它的原理時(shí),希望能夠幫助到你
者:雅克的一府
來源:http://www.52rd.com/Blog/Detail_RD.Blog_imjacob_4832.html
答案一:
1.異步傳輸
通常,異步傳輸是以字符為傳輸單位,每個(gè)字符都要附加 1 位起始位和 1 位停止位,以標(biāo)記一個(gè)字符的開始和結(jié)束,并以此實(shí)現(xiàn)數(shù)據(jù)傳輸同步。所謂異步傳輸是指字符與字符(一個(gè)字符結(jié)束到下一個(gè)字符開始)之間的時(shí)間間隔是可變的,并不需要嚴(yán)格地限制它們的時(shí)間關(guān)系。起始位對(duì)應(yīng)于二進(jìn)制值 0,以低電平表示,占用 1 位寬度。停止位對(duì)應(yīng)于二進(jìn)制值 1,以高電平表示,占用 1~2 位寬度。一個(gè)字符占用 5~8位,具體取決于數(shù)據(jù)所采用的字符集。例如,電報(bào)碼字符為 5 位、ASCII碼字符為 7 位、漢字碼則為8 位。此外,還要附加 1 位奇偶校驗(yàn)位,可以選擇奇校驗(yàn)或偶校驗(yàn)方式對(duì)該字符實(shí)施簡單的差錯(cuò)控制。發(fā)送端與接收端除了采用相同的數(shù)據(jù)格式(字符的位數(shù)、停止位的位數(shù)、有無校驗(yàn)位及校驗(yàn)方式等)外,還應(yīng)當(dāng)采用相同的傳輸速率。典型的速率有:9 600 b/s、19.2kb/s、56kb/s等。
異步傳輸又稱為起止式異步通信方式,其優(yōu)點(diǎn)是簡單、可靠,適用于面向字符的、低速的異步通信場(chǎng)合。例如,計(jì)算機(jī)與Modem之間的通信就是采用這種方式。它的缺點(diǎn)是通信開銷大,每傳輸一個(gè)字符都要額外附加2~3位,通信效率比較低。例如,在使用Modem上網(wǎng)時(shí),普遍感覺速度很慢,除了傳輸速率低之外,與通信開銷大、通信效率低也密切相關(guān)。
--------------------------------------------------------------------------------
2. 同步傳輸
通常,同步傳輸是以數(shù)據(jù)塊為傳輸單位。每個(gè)數(shù)據(jù)塊的頭部和尾部都要附加一個(gè)特殊的字符或比特序列,標(biāo)記一個(gè)數(shù)據(jù)塊的開始和結(jié)束,一般還要附加一個(gè)校驗(yàn)序列(如16位或32位CRC校驗(yàn)碼),以便對(duì)數(shù)據(jù)塊進(jìn)行差錯(cuò)控制。所謂同步傳輸是指數(shù)據(jù)塊與數(shù)據(jù)塊之間的時(shí)間間隔是固定的,必須嚴(yán)格地規(guī)定它們的時(shí)間關(guān)系。
答案二:
請(qǐng)講詳細(xì)一些,本人比較弱智,謝謝各位
---------------------------------------------------------------
同步是阻塞模式,異步是非阻塞模式。
---------------------------------------------------------------
我的理解:同步是指兩個(gè)線程的運(yùn)行是相關(guān)的,其中一個(gè)線程要阻塞等待另外一個(gè)線程的運(yùn)行。異步的意思是兩個(gè)線程毫無相關(guān),自己運(yùn)行自己的。
不知對(duì)錯(cuò)?樓下說
---------------------------------------------------------------
同步是指:發(fā)送方發(fā)出數(shù)據(jù)后,等接收方發(fā)回響應(yīng)以后才發(fā)下一個(gè)數(shù)據(jù)包的通訊方式。
異步是指:發(fā)送方發(fā)出數(shù)據(jù)后,不等接收方發(fā)回響應(yīng),接著發(fā)送下個(gè)數(shù)據(jù)包的通訊方式。
CSDN上有討論過:
http://expert.csdn.net/Expert/topic/2646/2646592.xml?temp=.3842584
http://expert.csdn.net/Expert/topic/2659/2659726.xml?temp=.1480219
---------------------------------------------------------------
舉個(gè)不太恰當(dāng)?shù)睦?就像:
SendMessage(...)
TRACE0("just like send");
PostMessage(...)
TRACE0("just like WSASend using overlapped");
SendMessage是調(diào)用的時(shí)候不返回,等消息響應(yīng)后才執(zhí)行TRACE0,這就是同步.
PostMessage是調(diào)用后馬上返回,不用消息響應(yīng)就執(zhí)行TRACE0,這就是異步.
答案三:
同步和異步的區(qū)別
舉個(gè)例子:普通B/S模式(同步)AJAX技術(shù)(異步)
同步:提交請(qǐng)求->等待服務(wù)器處理->處理完畢返回 這個(gè)期間客戶端瀏覽器不能干任何事
異步: 請(qǐng)求通過事件觸發(fā)->服務(wù)器處理(這是瀏覽器仍然可以作其他事情)->處理完畢
---------------------------------------------------------------
同步就是你叫我去吃飯,我聽到了就和你去吃飯;如果沒有聽到,你就不停的叫,直到我告訴你聽到了,才一起去吃飯。
異步就是你叫我,然后自己去吃飯,我得到消息后可能立即走,也可能等到下班才去吃飯。
所以,要我請(qǐng)你吃飯就用同步的方法,要請(qǐng)我吃飯就用異步的方法,這樣你可以省錢。
---------------------------------------------------------------
舉個(gè)例子 打電話時(shí)同步 發(fā)消息是異步
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。