自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

這就是所謂的JavaScript異步!

開發(fā) 前端
ECMAScript 6(簡稱ES6)將 JavaScript 異步編程帶入了一個全新的階段。這篇文章的主題,就是介紹更強(qiáng)大、更完善的 ES6 異步編程方法。

ECMAScript 6(簡稱ES6)將 JavaScript 異步編程帶入了一個全新的階段。這篇文章的主題,就是介紹更強(qiáng)大、更完善的 ES6 異步編程方法。

首先我們回顧一下javascript異步的發(fā)展歷程。

ES6 以前:

回調(diào)函數(shù)(callback):nodejs express 中常用,ajax中常用。

ES6:

promise對象:nodejs最早有bluebird promise的雛形,axios中常用。

generator函數(shù):nodejs koa框架使用率很高。

ES7:

async/await語法:當(dāng)前最常用的異步語法,nodejs koa2 完全使用該語法。

什么是異步

所謂"異步",簡單說就是一個任務(wù)分成兩段,先執(zhí)行***段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),等做好了準(zhǔn)備,再回過頭執(zhí)行第二段。比如,有一個任務(wù)是讀取文件進(jìn)行處理,異步的執(zhí)行過程就是下面這樣。

 

這就是所謂的JavaScript 異步!

 

異步

上圖中,任務(wù)的***段是向操作系統(tǒng)發(fā)出請求,要求讀取文件。然后,程序執(zhí)行其他任務(wù),等到操作系統(tǒng)返回文件,再接著執(zhí)行任務(wù)的第二段(處理文件)。

這種不連續(xù)的執(zhí)行,就叫做異步。相應(yīng)地,連續(xù)的執(zhí)行,就叫做同步。

 

這就是所謂的JavaScript 異步!

 

同步

上圖就是同步的執(zhí)行方式。由于是連續(xù)執(zhí)行,不能插入其他任務(wù),所以操作系統(tǒng)從硬盤讀取文件的這段時間,程序只能干等著。

回調(diào)函數(shù)callback

JavaScript 語言對異步編程的實(shí)現(xiàn),就是回調(diào)函數(shù)。所謂回調(diào)函數(shù),就是把任務(wù)的第二段單獨(dú)寫在一個函數(shù)里面,等到重新執(zhí)行這個任務(wù)的時候,就直接調(diào)用這個函數(shù)。它的英語名字 callback,直譯過來就是"重新調(diào)用"。

回調(diào)字面也好理解,就是先處理本體函數(shù),再處理回調(diào)的函數(shù),舉個例子,方便大家理解。

 

這就是所謂的JavaScript 異步!

 

上面的例子很好理解,首先執(zhí)行主體函數(shù)A,打印結(jié)果:我是主題函數(shù);

然后執(zhí)行回調(diào)函數(shù)callback 也就是B,打印結(jié)果:我是回調(diào)函數(shù)。

promise對象

promise 對象用于一個異步操作的最終完成(或最終失敗)及其結(jié)果的表示。

簡單地說就是處理一個異步請求。我們經(jīng)常會做些斷言,如果我贏了你就嫁給我,如果輸了我就嫁給你之類的斷言。

這就是promise的中文含義:斷言,一個成功,一個失敗。

舉個例子,方便大家理解:

promise構(gòu)造函數(shù)的參數(shù)是一個函數(shù),我們把它稱為處理器函數(shù)。

處理器函數(shù)接收兩個函數(shù)reslove和reject作為其參數(shù),當(dāng)異步操作順利執(zhí)行則執(zhí)行reslove函數(shù), 當(dāng)異步操作中發(fā)生異常時,則執(zhí)行reject函數(shù)。

通過resolve傳入得的值,可以在then方法中獲取到,通過reject傳入的值可以在chatch方法中獲取到。

因?yàn)閠hen和catch都返回一個相同的promise對象,所以可以進(jìn)行鏈?zhǔn)秸{(diào)用。

 

這就是所謂的JavaScript 異步!

 

Promise 的寫法只是回調(diào)函數(shù)的改進(jìn),使用then方法以后,異步任務(wù)的兩段執(zhí)行看得更清楚了,除此以外,并無新意。

Promise 的***問題是代碼冗余,原來的任務(wù)被Promise 包裝了一下,不管什么操作,一眼看去都是一堆 then,原來的語義變得很不清楚。

那么,有沒有更好的寫法呢?

協(xié)程

傳統(tǒng)的編程語言,早有異步編程的解決方案(其實(shí)是多任務(wù)的解決方案)。其中有一種叫做"協(xié)程"(coroutine),意思是多個線程互相協(xié)作,完成異步任務(wù)。

協(xié)程有點(diǎn)像函數(shù),又有點(diǎn)像線程。它的運(yùn)行流程大致如下。

***步,協(xié)程A開始執(zhí)行。

第二步,協(xié)程A執(zhí)行到一半,進(jìn)入暫停,執(zhí)行權(quán)轉(zhuǎn)移到協(xié)程B。

第三步,(一段時間后)協(xié)程B交還執(zhí)行權(quán)。

第四步,協(xié)程A恢復(fù)執(zhí)行。

上面流程的協(xié)程A,就是異步任務(wù),因?yàn)樗殖蓛啥?或多段)執(zhí)行。

舉例來說,讀取文件的協(xié)程寫法如下。

 

  1. function asnycJob() { 
  2.  // ...其他代碼 
  3.  var f = yield readFile(fileA); 
  4.  // ...其他代碼 

上面代碼的函數(shù) asyncJob 是一個協(xié)程,它的奧妙就在其中的 yield 命令。它表示執(zhí)行到此處,執(zhí)行權(quán)將交給其他協(xié)程。也就是說,yield命令是異步兩個階段的分界線。

協(xié)程遇到 yield 命令就暫停,等到執(zhí)行權(quán)返回,再從暫停的地方繼續(xù)往后執(zhí)行。它的***優(yōu)點(diǎn),就是代碼的寫法非常像同步操作,如果去除yield命令,簡直一模一樣。

Generator 函數(shù)

Generator 函數(shù)是協(xié)程在 ES6 的實(shí)現(xiàn),***特點(diǎn)就是可以交出函數(shù)的執(zhí)行權(quán)(即暫停執(zhí)行)。

 

  1. function* gen(x){ 
  2.  var y = yield x + 2; 
  3.  return y; 

上面代碼就是一個 Generator 函數(shù)。它不同于普通函數(shù),是可以暫停執(zhí)行的,所以函數(shù)名之前要加星號,以示區(qū)別。

整個 Generator 函數(shù)就是一個封裝的異步任務(wù),或者說是異步任務(wù)的容器。異步操作需要暫停的地方,都用 yield 語句注明。Generator 函數(shù)的執(zhí)行方法如下。

 

  1. var g = gen(1); 
  2. g.next() // { value: 3, done: false } 
  3. g.next() // { value: undefined, done: true } 

上面代碼中,調(diào)用 Generator 函數(shù),會返回一個內(nèi)部指針(即遍歷器 )g 。這是 Generator 函數(shù)不同于普通函數(shù)的另一個地方,即執(zhí)行它不會返回結(jié)果,返回的是指針對象。調(diào)用指針 g 的 next 方法,會移動內(nèi)部指針(即執(zhí)行異步任務(wù)的***段),指向***個遇到的 yield 語句,上例是執(zhí)行到 x + 2 為止。

換言之,next 方法的作用是分階段執(zhí)行 Generator 函數(shù)。每次調(diào)用 next 方法,會返回一個對象,表示當(dāng)前階段的信息( value 屬性和 done 屬性)。value 屬性是 yield 語句后面表達(dá)式的值,表示當(dāng)前階段的值;done 屬性是一個布爾值,表示 Generator 函數(shù)是否執(zhí)行完畢,即是否還有下一個階段。

Generator 函數(shù)的用法

下面看看如何使用 Generator 函數(shù),執(zhí)行一個真實(shí)的異步任務(wù)。

 

  1. var fetch = require('node-fetch'); 
  2. function* gen(){ 
  3.  var url = 'https://api.github.com/users/github'
  4.  var result = yield fetch(url); 
  5.  console.log(result.bio); 

上面代碼中,Generator 函數(shù)封裝了一個異步操作,該操作先讀取一個遠(yuǎn)程接口,然后從 JSON 格式的數(shù)據(jù)解析信息。就像前面說過的,這段代碼非常像同步操作,除了加上了 yield 命令。

執(zhí)行這段代碼的方法如下。

 

  1. var g = gen(); 
  2. var result = g.next(); 
  3. result.value.then(function(data){ 
  4.  return data.json(); 
  5. }).then(function(data){ 
  6.  g.next(data); 
  7. }); 

上面代碼中,首先執(zhí)行 Generator 函數(shù),獲取遍歷器對象,然后使用 next 方法(第二行),執(zhí)行異步任務(wù)的***階段。由于 Fetch 模塊返回的是一個 Promise 對象,因此要用 then 方法調(diào)用下一個next 方法。

可以看到,雖然 Generator 函數(shù)將異步操作表示得很簡潔,但是流程管理卻不方便(即何時執(zhí)行***階段、何時執(zhí)行第二階段)。

async-await

async函數(shù)返回一個promise對象,如果在async函數(shù)中返回一個直接量,async會通過Promise.resolve封裝成Promise對象。

我們可以通過調(diào)用promise對象的then方法,獲取這個直接量。

 

這就是所謂的JavaScript 異步!

 

那如過async函數(shù)不返回值,又會是怎么樣呢?

 

這就是所謂的JavaScript 異步!

 

await會暫停當(dāng)前async的執(zhí)行,await會阻塞代碼的執(zhí)行,直到await后的表達(dá)式處理完成,代碼才能繼續(xù)往下執(zhí)行。

await后的表達(dá)式既可以是一個Promise對象,也可以是任何要等待的值。

如果await等到的是一個 Promise 對象,await 就忙起來了,它會阻塞后面的代碼,等著 Promise 對象 resolve,然后得到 resolve 的值,作為 await 表達(dá)式的運(yùn)算結(jié)果。

上邊你看到阻塞一詞,不要驚慌,async/await只是一種語法糖,代碼執(zhí)行與多個callback嵌套調(diào)用沒有區(qū)別。

本質(zhì)并不是同步代碼,它只是讓你思考代碼邏輯的時候能夠以同步的思維去思考,避開回調(diào)地獄。

簡而言之-async/await是以同步的思維去寫異步的代碼,所以async/await并不會影響node的并發(fā)數(shù),大家可以大膽的應(yīng)用到項(xiàng)目中去!

如果它等到的不是一個 Promise 對象,那 await 表達(dá)式的運(yùn)算結(jié)果就是它等到的東西。

舉個例子,方便大家理解:

 

這就是所謂的JavaScript 異步!  

責(zé)任編輯:龐桂玉 來源: 今日頭條
相關(guān)推薦

2015-07-21 10:24:02

Windows RT升級

2014-01-02 14:04:42

2021-09-03 10:44:42

ThreadLocalObject 數(shù)組

2019-01-02 04:40:19

物聯(lián)網(wǎng)企業(yè)IOT

2024-12-13 16:37:56

SpringBootJava

2016-01-12 17:01:45

Bootstrap原因

2015-07-27 10:56:02

2013-07-26 10:45:20

2020-02-17 15:55:22

Office 365

2024-04-24 09:47:36

2018-11-01 13:38:51

Java中斷停止

2015-01-09 10:10:00

Linux

2020-07-17 19:31:19

PythonR編程

2015-09-19 13:45:27

2023-06-28 15:17:12

數(shù)字化轉(zhuǎn)型數(shù)字化

2022-11-21 16:10:31

奔馳可靠性排名

2021-08-17 13:31:11

加密貨幣區(qū)塊鏈貨幣

2021-06-15 11:16:49

代碼耦合開發(fā)

2023-07-13 09:00:00

人工智能GPT模型

2019-11-27 08:52:13

網(wǎng)易裁員微博
點(diǎn)贊
收藏

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