成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Javascript的異步編程知多少?

開發(fā) 前端
本文主要介紹了Javascript的最重要的知識點(diǎn)之一,也是之后開發(fā)工作中經(jīng)常要接觸的概念,常用的異步編程方式有:回調(diào)函數(shù)、Promise、Generator和async/await。頻繁使用回調(diào)函數(shù)會造成回調(diào)地獄,Promise的出現(xiàn)就是解決回調(diào)地獄的,但是Promise的鏈?zhǔn)胶瘮?shù)也有長,對于出現(xiàn)了async/await的終極解決方案。

[[439755]]

1寫在前面

Generator執(zhí)行后返回什么?

Async/await的方式比Promise和Generatir好在哪里?

2同步和異步

同步:就是在執(zhí)行某段代碼時,在該代碼沒有得到返回結(jié)果前,其它代碼是阻塞的無法執(zhí)行,但是一旦執(zhí)行完成拿到返回值后,就可以執(zhí)行其它代碼了。

異步:就是當(dāng)某段代碼執(zhí)行異步過程調(diào)用發(fā)出后,這段代碼不會立即得到返回結(jié)果,而是掛起在后臺執(zhí)行。在異步調(diào)用發(fā)出后,一般通過回調(diào)函數(shù)處理這個調(diào)用后才能拿到結(jié)果。

前面知道Javascript是單線程的,如果JS都是同步代碼執(zhí)行可能會造成阻塞。如果使用就不會造成阻塞,就不需要等待異步代碼執(zhí)行的返回結(jié)果,可以繼續(xù)執(zhí)行該異步任務(wù)之后的代碼邏輯。

那么JS異步編程的實(shí)現(xiàn)方式是如何發(fā)展的呢?

早些年為了實(shí)現(xiàn)JS的異步編程,一般采用回調(diào)函數(shù)的方式,如:比較典型的事件回調(diào),但是使用回調(diào)函數(shù)來實(shí)現(xiàn)存在一個很常見的問題,就是回調(diào)地獄。看下面的代碼像不像俄羅斯套娃。

  1. fs.readFile(a,"utf-8",(err,data)=>{ 
  2.     fs.readFile(b,"utf-8",(err,data)=>{ 
  3.         fs.readFile(c,"utf-8",(err,data)=>{ 
  4.             fs.readFile(d,"utf-8",(err,data)=>{ 
  5.                     .... 
  6.             }) 
  7.         }) 
  8.     }) 
  9. }) 

常見的異步編程的場景有:

  • ajax請求的回調(diào)
  • 定時器中的回調(diào)
  • 事件回調(diào)
  • Node.js中的一些方法回調(diào)

異步回調(diào)如果層級很少,可讀性和代碼的維護(hù)性暫時還是可以接受的,但是當(dāng)層級變多后就會陷入回調(diào)地獄。

3Promise

為了解決回調(diào)地獄的問題,社區(qū)提出了Promise的解決方案,ES6又將其寫入語言標(biāo)準(zhǔn),采用Promise的實(shí)現(xiàn)方式在一定程度上解決了回調(diào)地獄的問題。

Promise簡單理解就是一個容器,里面保存了某個未來才會結(jié)束的事件的結(jié)果。從語法而言,Promise是一個可以獲取異步操作消息的對象。Promise具有三個狀態(tài):

  • 待定狀態(tài)pending:初始狀態(tài),既沒有被完成,也沒有被拒絕
  • 已完成fulfilled:操作成功完成
  • 已拒絕rejected:操作失敗

關(guān)于Promise的狀態(tài)切換,如果想深入研究,可以學(xué)習(xí)『有限狀態(tài)機(jī)』知識點(diǎn)。

待定狀態(tài)的Promise對象執(zhí)行的話,最后要么通過一個值完成,要么就是通過一個原因拒絕。當(dāng)待定狀態(tài)改成為完成或拒絕狀態(tài)時,我們可以使用Promise.then的形式進(jìn)行鏈?zhǔn)秸{(diào)用。因為最后Promise.prototype.then和Promise.prototype.catch方法返回的是一個Promise,所以它們可以繼續(xù)被鏈?zhǔn)秸{(diào)用。

Promise是如何結(jié)局回調(diào)地獄問題的?

  • 解決多層嵌套問題
  • 每種任務(wù)的處理結(jié)果存在兩種可能性(成功或失敗),那么需要在每種任務(wù)執(zhí)行結(jié)束后分別處理這兩種可能性

Promise主要利用三大技術(shù)來解決回調(diào)地獄:回調(diào)函數(shù)延遲綁定、返回值穿透、錯誤冒泡

Promise.all

Promise.all(iterable)可以傳遞一個可迭代對象作為參數(shù),此方法對于匯總多個Promise的結(jié)果很有用,在es6中可以將多個Promise.all異步請求并行操作。當(dāng)所有結(jié)果成功返回時按照順序返回成功,當(dāng)其中一個方法失敗則進(jìn)入失敗方法。

  1. Promise.all(iterable); 

使用Promise.all解決上面的異步編程問題。

  1. function read(url){ 
  2.  
  3. return new Promise((resolve,reject)=>{ 
  4.  
  5. fs.readFile(url,"utf-8",(err,data)=>{ 
  6.  
  7. if(err) return err; 
  8.  
  9. resolve(data); 
  10.  
  11. }) 
  12.  
  13. }) 
  14.  
  15.  
  16. read(A).then(data=>{ 
  17.  
  18. return read(B); 
  19.  
  20. }).then(data=>{ 
  21.  
  22. return read(C); 
  23.  
  24. }).then(data=>{ 
  25.  
  26. return read(D); 
  27.  
  28. }).catch(reason=>{ 
  29.  
  30. console.log(reason); 
  31.  
  32. }) 

我們看到上面使用Promise的使用對回調(diào)地獄的解決有所提升,但是依舊不是很好維護(hù),對此有了新的方法。

  1. function read(url){ 
  2.   return new Promise((resolve,reject)=>{ 
  3.     fs.readFile(url,"utf-8",(err,data)=>{ 
  4.       if(err) return err; 
  5.       resolve(data); 
  6.     }) 
  7.   }) 
  8. //通過Promise.all可以實(shí)現(xiàn)多個異步并行執(zhí)行,同一時刻獲取最終解決的問題 
  9. Promise.all([read(A),read(B),read(C)]).(data=>{ 
  10.     console.log(data) 
  11. }).catch(reason=>{ 
  12.     console.log(reason); 
  13. }) 

Promise.allSettled

Promise.allSettled的語法和Promise.all類似,都是接受一個可迭代對象作為參數(shù),返回一個新的Promise。當(dāng)Promise.allSettled全部處理完畢后,我們可以拿到每個Promise的狀態(tài),而不管其是否處理成功。

  1. Promise.allSettled(iterable); 

Promise.any

Promise.any也是接收一個可迭代對象作為參數(shù),any方法返回一個Promise。只要參數(shù)Promise實(shí)例有一個變成fulfilled狀態(tài),最后any返回的實(shí)例就會變成fullfiled狀態(tài);如果所有參數(shù)Promise實(shí)例都變成rejected狀態(tài),最后any返回的實(shí)例就會變成rejected狀態(tài)。

Promise.race

Promise.race接收一個可迭代對象作為參數(shù),race方法返回一個Promise,只要參數(shù)之中有一個實(shí)例率先改變狀態(tài),則race方法的返回狀態(tài)就跟著改變。

Promise方法 作用
all 參數(shù)所有返回結(jié)果都為成功才返回
allSettled 參數(shù)無論返回結(jié)果是否成功,都返回每個參數(shù)執(zhí)行狀態(tài)
any 參數(shù)中只要有一個成功,就返回該成功的執(zhí)行結(jié)果
race 返回最先執(zhí)行成功的參數(shù)的執(zhí)行結(jié)果

4Generator

Generator生成器是es6的新關(guān)鍵詞,Generator是一個帶星號的函數(shù),可以配合yield關(guān)鍵字來暫停或執(zhí)行函數(shù)。

Generator最大的特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán),Generator函數(shù)可以看作是異步任務(wù)的容器,需要暫停的地方使用yield語法進(jìn)行標(biāo)注。

  1. function* gen(){ 
  2.   let a = yield 111; 
  3.   console.log(a); 
  4.   let b = yield 222; 
  5.   console.log(b); 
  6.   let c = yield 333; 
  7.   console.log(c); 
  8.   let d = yield 444; 
  9.   console.log(d); 
  10.  
  11. let t = gen(); 
  12. t.next(1);//第一調(diào)用next函數(shù)時,傳遞的參數(shù)無效,因此無法打印結(jié)果 
  13. t.next(2);//2 
  14. t.next(3);//3 
  15. t.next(4);//4 
  16. t.next(5);//5 

上面代碼中,調(diào)用gen()后程序會被阻塞住,不會執(zhí)行任何語句;而調(diào)用g.next()后程序會繼續(xù)執(zhí)行,直到遇到y(tǒng)ield關(guān)鍵詞時執(zhí)行暫停;一直執(zhí)行next方法,最后返回一個對象,其存在兩個屬性:value和done。

yield也是es6的關(guān)鍵詞,配合Generator執(zhí)行以及暫停,yield關(guān)鍵詞最后返回一個迭代器對象,該對象有value和done兩個屬性,value表示返回的值,done便是當(dāng)前是否完成。

  1. function* gen(){ 
  2.   yield 1; 
  3.   yield* gen2(); 
  4.   yield 4; 
  5.  
  6. function* gen2(){ 
  7.   yield 2; 
  8.   yield 3; 
  9.  
  10. const g = gen(); 
  11. console.log(g.next()); 
  12. console.log(g.next()); 
  13. console.log(g.next()); 
  14. console.log(g.next()); 

運(yùn)行結(jié)果:

那么,Generator和異步編程有著什么聯(lián)系呢?澤呢么才能將Generator函數(shù)按照順序一次執(zhí)行完畢呢?

thunk函數(shù)

thunk函數(shù)的基本思路就是接收一定的參數(shù),會產(chǎn)生觸定制化的函數(shù),最后使用定制化的函數(shù)去完成想要實(shí)現(xiàn)的功能。

  1. const isType = type => { 
  2.   return obj => { 
  3.     return Object.prototype.toString.call(obj) === `[object ${type}]`; 
  4.   } 
  5.  
  6. const isString = isType("string"); 
  7. const isArray = isType("Array"); 
  8.  
  9. isString("yichuan");//true 
  10. isArray(["red","green","blue"]);//true 
  1. const readFileThunk = filename=>{  
  2.   return callback=>{  
  3.     fs.readFile(filename,callback);  
  4.   }  
  5. }  
  6.   
  7. const gen = function* (){  
  8.   const data1 = yield readFileThunk("a.txt");  
  9.   console.log(data1.toString());  
  10.   const data2 = yield readFileThunk("b.txt");  
  11.   console.log(data2.toString());  
  12. }  
  13.   
  14. const g = gen();  
  15. g.next().value((err,data1)=>{   
  16.   g.next(data1).value((err,data2)=>{  
  17.      g.next(data2);  
  18.   })  
  19. })  

我們可以看到上面的代碼還是像俄羅斯套娃,理解費(fèi)勁,我們進(jìn)行優(yōu)化以下:

  1. function fun(get){ 
  2.   const next = (err,data)=>{ 
  3.     const res = gen.next(data); 
  4.     if(res.done) return
  5.     res.value(next); 
  6.   } 
  7.   next(); 
  8.  
  9. run(g); 

co函數(shù)庫是用于處理Generator函數(shù)的自動執(zhí)行,核心原理是前面講到的通過和thunk函數(shù)以及Promise對象進(jìn)行配合,包裝成一個庫。

Generator函數(shù)就是一個異步操作的容器,co函數(shù)接收Generator函數(shù)作為參數(shù),并最后返回一個Promise對象。在返回的Promise對象中,co先檢查參數(shù)gen是否為Generator函數(shù)。如果是就執(zhí)行函數(shù),如果不是就直接返回,并將Promise對象的狀態(tài)改為resolved。co將Generator函數(shù)的內(nèi)部指針對象的next方法包裝成onFulfilled函數(shù),主要是為了能夠捕獲到拋出的錯誤。關(guān)鍵在于next,他會反復(fù)調(diào)用自身。

  1. const co = require("co"); 
  2.  
  3. const g = gen(); 
  4.  
  5. co(g).then(res=>{ 
  6.  
  7. console.log(res); 
  8.  
  9. }) 

5Async/await

JS異步編程從最開始的回調(diào)函數(shù)的方式演化到使用Promise對象,再到Generator+co函數(shù)的方式,每次都有一些改變但是都不徹底。async/await被稱為JS中異步終極解決方案,既能夠像Generator+co函數(shù)一樣用同步方式阿里寫異步代碼,又能夠得到底層的語法支持,無需借助任何第三方庫。

async是Generator函數(shù)的語法糖,async/await的優(yōu)點(diǎn)是代碼清晰,可以處理回調(diào)的問題。

  1. function testWait(){ 
  2.   return new Promise((resolve,reject)=>{ 
  3.     setTimeout(()=>{ 
  4.       console.log("testWait"); 
  5.       resolve(); 
  6.     },1000); 
  7.   }) 
  8.  
  9. async function testAwaitUse(){ 
  10.   await testWait(); 
  11.   console.log("hello"); 
  12.   return "yichuan"
  13. //輸出順序依次是:testWait hello yichuan 
  14. console.log(testAwaitUse()); 

6異步編程方式小結(jié)

JS異步編程方式 簡單總結(jié)
回調(diào)函數(shù) 最拉胯的異步編程方式
Promise es6新增語法,解決回調(diào)地獄問題
Generator 和yield配合使用,返回的是迭代器
async/await 二者配合使用,async返回的是Promise對象,await控制執(zhí)行順序

7參考文章

《Javascript核心原理精講》

《Javascript高級程序設(shè)計》

《你不知道的Javascrtipt》

《JS 異步編程六種方案》

8寫在最后

 

本文主要介紹了Javascript的最重要的知識點(diǎn)之一,也是之后開發(fā)工作中經(jīng)常要接觸的概念,常用的異步編程方式有:回調(diào)函數(shù)、Promise、Generator和async/await。頻繁使用回調(diào)函數(shù)會造成回調(diào)地獄,Promise的出現(xiàn)就是解決回調(diào)地獄的,但是Promise的鏈?zhǔn)胶瘮?shù)也有長,對于出現(xiàn)了async/await的終極解決方案。

 

責(zé)任編輯:武曉燕 來源: 前端萬有引力
相關(guān)推薦

2021-12-04 11:17:32

Javascript繼承編程

2021-12-11 18:59:35

JavascriptJSON應(yīng)用

2021-12-03 15:24:45

Javascript數(shù)據(jù)類型

2013-07-15 15:35:06

2021-12-07 08:01:33

Javascript 垃圾回收機(jī)制前端

2021-12-05 08:27:56

Javascript 高階函數(shù)前端

2020-10-15 13:29:57

javascript

2021-12-06 07:15:48

Javascript作用域閉包

2012-02-13 22:50:59

集群高可用

2024-08-06 10:07:15

2017-07-13 12:12:19

前端JavaScript異步編程

2016-09-07 20:43:36

Javascript異步編程

2015-04-22 10:50:18

JavascriptJavascript異

2014-05-23 10:12:20

Javascript異步編程

2023-08-23 13:24:00

異步編程方法

2021-03-19 10:14:28

SpringBoot項目異步調(diào)用

2022-05-08 18:02:11

tunnel隧道云原生

2011-11-11 15:47:22

JavaScript

2010-08-16 09:15:57

2013-12-23 14:00:31

Windows 8.2Windows 8.1
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 97视频网站 | 欧美成人免费在线视频 | 亚洲一区二区三区视频 | 人人干免费 | 精品美女久久久久久免费 | 成人一区二区三区在线观看 | 中文字幕日韩欧美 | 午夜理伦三级理论三级在线观看 | 亚洲综合视频一区 | 激情黄色在线观看 | 精品一区二区三区在线视频 | 久久精品国产一区二区三区不卡 | 色www精品视频在线观看 | www日日日 | 婷婷在线视频 | 美国一级片在线观看 | 一级大片 | 亚洲精品日本 | 五月激情综合 | 国产精品www| 亚洲综合色自拍一区 | 福利久久 | 色综合久久伊人 | 91一区二区三区在线观看 | 免费在线性爱视频 | 亚州成人| 午夜爽爽男女免费观看hd | 一区在线免费视频 | 久久伊人精品 | 成人网在线看 | 久久69精品久久久久久国产越南 | 国产成人精品亚洲日本在线观看 | 亚洲精品一区在线 | 成人av观看 | 国产免费人成xvideos视频 | 午夜电影一区二区 | 国产精品免费一区二区三区四区 | 亚洲精品福利在线 | 成人伊人网 | 国产成人在线视频 | 欧美8一10sex性hd |