真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

JavaScript異步編程使用的方法

本篇內(nèi)容介紹了“JavaScript異步編程使用的方法”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)服務(wù)項目包括曹妃甸網(wǎng)站建設(shè)、曹妃甸網(wǎng)站制作、曹妃甸網(wǎng)頁制作以及曹妃甸網(wǎng)絡(luò)營銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢、行業(yè)經(jīng)驗、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,曹妃甸網(wǎng)站推廣取得了明顯的社會效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到曹妃甸省份的部分城市,未來相信會繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

按照維基百科上的解釋:獨(dú)立于主控制流之外發(fā)生的事件就叫做異步。比如說有一段順序執(zhí)行的代碼

void function main() {   fA();   fB(); }();

fA => fB 是順序執(zhí)行的,永遠(yuǎn)都是 fA 在 fB 的前面執(zhí)行,他們就是 同步 的關(guān)系。加入這時使用 setTimeout 將 fB  延后

void function main() {   setTimeout(fA, 1000);   fB(); }();

這時,fA 相對于 fB 就是異步的。main 函數(shù)只是聲明了要在一秒后執(zhí)行一次 fA,而并沒有立刻執(zhí)行它。這時,fA 的控制流就獨(dú)立于 main  之外。

JavaScript——天生異步的語言

因為 setTimeout 的存在,至少在被 ECMA 標(biāo)準(zhǔn)化的那一刻起,JavaScript 就支持異步編程了。與其他語言的 sleep  不同,setTimeout 是異步的——它不會阻擋當(dāng)前程序繼續(xù)往下執(zhí)行。

然而異步編程真正發(fā)展壯大,Ajax 的流行功不可沒。Ajax 中的 A(Asynchronous)真正點到了異步的概念——這還是 IE5、IE6  的時代。

回調(diào)函數(shù)——異步編程之痛

異步任務(wù)執(zhí)行完畢之后怎樣通知開發(fā)者呢?回調(diào)函數(shù)是最樸素的,容易想到的實現(xiàn)方式。于是從異步編程誕生的那一刻起,它就和回調(diào)函數(shù)綁在了一起。

例如 setTimeout。這個函數(shù)會起一個定時器,在超過指定時間后執(zhí)行指定的函數(shù)。比如在一秒后輸出數(shù)字 1,代碼如下:

setTimeout(() => {   console.log(1); }, 1000);

常規(guī)用法。如果需求有變,需要每秒輸出一個數(shù)字(當(dāng)然不是用 setInterval),JavaScript 的初學(xué)者可能會寫出這樣的代碼:

for (let i = 1; i < 10; ++i) {   setTimeout(() => { // 錯誤!     console.log(i);   }, 1000); }

執(zhí)行結(jié)果是等待 1 秒后,一次性輸出了所有結(jié)果。因為這里的循環(huán)是同時啟了 10 個定時器,每個定時器都等待 1 秒,結(jié)果當(dāng)然是所有定時器在 1  秒后同時超時,觸發(fā)回調(diào)函數(shù)。

解法也簡單,只需要在前一個定時器超時后再啟動另一個定時器,代碼如下:

setTimeout(() => {   console.log(1);   setTimeout(() => {     console.log(2);     setTimeout(() => {       console.log(3);       setTimeout(() => {         console.log(4);         setTimeout(() => {           console.log(5);           setTimeout(() => {             // ...           }, 1000);         }, 1000);       }, 1000)     }, 1000)   }, 1000) }, 1000);

層層嵌套,結(jié)果就是這樣的漏斗形代碼??赡苡腥讼氲搅诵聵?biāo)準(zhǔn)中的 Promise,可以改寫如下:

function timeout(delay) {   return new Promise(resolve => {     setTimeout(resolve, delay);   }); }  timeout(1000).then(() => {   console.log(1);   return timeout(1000); }).then(() => {   console.log(2);   return timeout(1000); }).then(() => {   console.log(3);   return timeout(1000); }).then(() => {   console.log(4);   return timeout(1000); }).then(() => {   console.log(5);   return timeout(1000); }).then(() => {   // .. });

漏斗形代碼是沒了,但代碼量本身并沒減少多少。Promise 并沒能干掉回調(diào)函數(shù)。

因為回調(diào)函數(shù)的存在,循環(huán)就無法使用。不能循環(huán),那么只能考慮遞歸了,解法如下:

let i = 1; function next() {   console.log(i);   if (++i < 10) {     setTimeout(next, 1000);   } } setTimeout(next, 1000);

注意雖然寫法是遞歸,但由于 next 函數(shù)都是由瀏覽器調(diào)用的,所以實際上并沒有遞歸函數(shù)的調(diào)用棧結(jié)構(gòu)。

Generator——JavaScript 中的半?yún)f(xié)程

很多語言都引入了協(xié)程來簡化異步編程,JavaScript 也有類似的概念,叫做 Generator。

MDN 上的解釋:Generator 是一種可以中途退出之后重入的函數(shù)。他們的函數(shù)上下文在每次重入后會被保持。簡而言之,Generator 與普通  Function ***的區(qū)別就是:Generator 自身保留上次調(diào)用的狀態(tài)。

舉個簡單的例子:

function *gen() {   yield 1;   yield 2;   return 3; }  void function main() {   var iter = gen();   console.log(iter.next().value);   console.log(iter.next().value);   console.log(iter.next().value); }();

代碼的執(zhí)行順序是這樣:

  1. 請求 gen,得到一個迭代器 iter。注意此時并未真正執(zhí)行 gen 的函數(shù)體。

  2. 調(diào)用 iter.next(),執(zhí)行 gen 的函數(shù)體。

  3. 遇到 yield 1,將 1 返回,iter.next() 的返回值即為 { done: false, value: 1 },輸出 1

  4. 調(diào)用 iter.next()。從上次 yield 出去的地方繼續(xù)往下執(zhí)行 gen。

  5. 遇到 yield 2,將 2 返回,iter.next() 的返回值即為 { done: false, value: 2 },輸出 2

  6. 調(diào)用 iter.next()。從上次 yield 出去的地方繼續(xù)往下執(zhí)行 gen。

  7. 遇到 return 3,將 3 返回,return 表示整個函數(shù)已經(jīng)執(zhí)行完畢。iter.next() 的返回值即為 { done: true,  value: 3 },輸出 3

調(diào)用 Generator 函數(shù)只會返回一個迭代器,當(dāng)用戶主動調(diào)用了 iter.next() 后,這個 Generator 函數(shù)才會真正執(zhí)行。

你可以使用 for ... of 遍歷一個 iterator,例如

for (var i of gen()) {   console.log(i); }

輸出 1 2,*** return 3 的結(jié)果不算在內(nèi)。想用 Generator 的各項生成一個數(shù)組也很簡單,Array.from(gen()) 或直接用  [...gen()] 即可,生成 [1, 2] 同樣不包含***的 return 3。

Generator 是異步的嗎

Generator 也叫半?yún)f(xié)程(semicoroutine),自然與異步關(guān)系匪淺。那么 Generator 是異步的嗎?

既是也不是。前面提到,異步是相對的,例如上面的例子

function *gen() {   yield 1;   yield 2;   return 3; }  void function main() {   var iter = gen();   console.log(iter.next().value);   console.log(iter.next().value);   console.log(iter.next().value); }();

我們可以很直觀的看到,gen 的方法體與 main 的方法體在交替執(zhí)行,所以可以肯定的說,gen 相對于 main  是異步執(zhí)行的。然而此段過程中,整個控制流都沒有交回給瀏覽器,所以說 gen 和 main 相對于瀏覽器是同步執(zhí)行的。

用 Generator 簡化異步代碼

回到最初的問題:

for (let i = 0; i < 10; ++i) {   setTimeout(() => {     console.log(i);   }, 1000);   // 等待上面 setTimeout 執(zhí)行完畢 }

關(guān)鍵在于如何等待前面的 setTimeout 觸發(fā)回調(diào)后再執(zhí)行下一輪循環(huán)。如果使用 Generator,我們可以考慮在 setTimeout 后  yield 出去(控制流返還給瀏覽器),然后在 setTimeout 觸發(fā)的回調(diào)函數(shù)中 next,將控制流交還回給代碼,執(zhí)行下一段循環(huán)。

let iter;  function* run() {   for (let i = 1; i < 10; ++i) {     setTimeout(() => iter.next(), 1000);     yield; // 等待上面 setTimeout 執(zhí)行完畢     console.log(i);   } }  iter = run(); iter.next();

代碼的執(zhí)行順序是這樣:

  • 請求 run,得到一個迭代器 iter。注意此時并未真正執(zhí)行 run 的函數(shù)體。

  • 調(diào)用 iter.next(),執(zhí)行 run 的函數(shù)體。

  • 循環(huán)開始,i 初始化為 1。

  • 執(zhí)行 setTimeout,啟動一個定時器,回調(diào)函數(shù)延后 1 秒執(zhí)行。

  • 遇到 yield(即 yield undefined),控制流返回到***的 iter.next()  之后。因為后面沒有其他代碼了,瀏覽器獲得控制權(quán),響應(yīng)用戶事件,執(zhí)行其他異步代碼等。

  • 1 秒后,setTimeout 超時,執(zhí)行回調(diào)函數(shù) () => iter.next()。

  • 調(diào)用 iter.next()。從上次 yield 出去的地方繼續(xù)往下執(zhí)行,即 console.log(i),輸出 i 的值。

  • 一次循環(huán)結(jié)束,i 自增為 2,回到第 4 步繼續(xù)執(zhí)行

  • ……

這樣即實現(xiàn)了類似同步 sleep 的要求。

async、await——用同步語法寫異步代碼

上面的代碼畢竟需要手工定義迭代器變量,還要手工 next;更重要的是與 setTimeout 緊耦合,無法通用。

我們知道 Promise 是異步編程的未來。能不能把 Promise 和 Generator 結(jié)合使用呢?這樣考慮的結(jié)果就是 async 函數(shù)。

用 async 得到代碼如下

function timeout(delay) {   return new Promise(resolve => {     setTimeout(resolve, delay);   }); }  async function run() {   for (let i = 1; i < 10; ++i) {     await timeout(1000);     console.log(i);   } } run();

按照 Chrome 的設(shè)計文檔,async 函數(shù)內(nèi)部就是被編譯為 Generator 執(zhí)行的。run 函數(shù)本身會返回一個  Promise,用于使主調(diào)函數(shù)得知 run 函數(shù)什么時候執(zhí)行完畢。所以 run() 后面也可以 .then(xxx),甚至直接 await run()。

注意有時候我們的確需要幾個異步事件并行執(zhí)行(比如調(diào)用兩個接口,等兩個接口都返回后執(zhí)行后續(xù)代碼),這時就不要過度使用 await,例如:

const a = await queryA(); // 等待 queryA 執(zhí)行完畢后 const b = await queryB(); // 執(zhí)行 queryB doSomething(a, b);

這時 queryA 和 queryB 就是串行執(zhí)行的??梢月宰餍薷模?/p>

const promiseA = queryA(); // 執(zhí)行 queryA const b = await queryB(); // 執(zhí)行 queryB 并等待其執(zhí)行結(jié)束。這時同時 queryA 也在執(zhí)行。 const a = await promiseA(); // 這時 queryB 已經(jīng)執(zhí)行結(jié)束。繼續(xù)等待 queryA 執(zhí)行結(jié)束 doSomething(a, b);

我個人比較喜歡如下寫法:

const [ a, b ] = await Promise.all([ queryA(), queryB() ]); doSomething(a, b);

將 await 和 Promise 結(jié)合使用,效果更佳!

“JavaScript異步編程使用的方法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!


當(dāng)前文章:JavaScript異步編程使用的方法
瀏覽路徑:http://weahome.cn/article/iepcec.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部