中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

vue中async-await如何使用

發布時間:2021-07-09 15:15:00 來源:億速云 閱讀:392 作者:Leah 欄目:web開發

本篇文章為大家展示了vue中async-await如何使用,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

曾經見過為了讓鉤子函數的異步代碼可以同步執行,而對鉤子函數使用async/await,就好像下面的代碼:

// exp-01
export default {
 async created() {
 const timeKey = 'cost';
 console.time(timeKey);
 console.log('start created');
 this.list = await this.getList();
 console.log(this.list);
 console.log('end created');
 console.timeEnd(timeKey);
 },
 mounted() {
 const timeKey = 'cost';
 console.time(timeKey);
 console.log('start mounted');
 console.log(this.list.rows);
 console.log('end mounted');
 console.timeEnd(timeKey);
 },
 data() {
 return {
  list: []
 };
 },
 methods: {
 getList() {
  return new Promise((resolve) => {
  setTimeout(() => {
   return resolve({
   rows: [
    { name: 'isaac', position: 'coder' }
   ]
   });
  }, 3000);
  });
 }
 }
};

exp-01 的代碼最后會輸出:

start created
start mounted
undefined
end mounted
mounted cost: 2.88623046875ms
{__ob__: Observer}
end created
created cost: 3171.545166015625ms

很明顯沒有達到預期的效果,為什么?

根據 exp-01 的輸出結果,可以看出代碼的執行順序,首先是鉤子的執行順序:

created => mounted

是的,鉤子的執行順序還是正常的沒有被打亂,證據就是:created鉤子中的同步代碼是在mounted先執行的:

start created
start mounted

再看看created鉤子內部的異步代碼:

this.list = await this.getList();

可以看見this.list的打印結果

end mounted
mounted cost: 2.88623046875ms
// 這是created鉤子打印的this.list
{__ob__: Observer}
end created

在mounted鉤子執行完畢之后才打印,言外之意是使用async/await的鉤子內部的異步代碼并沒有起到阻塞鉤子主線程的執行。這里說的鉤子函數的主線程是指:

beforeCreate => created => beforeMount => mounted => ...

會寫出以上代碼的原因我估計有兩個:

exp-01

正文

剖析一下

前言中針對代碼的執行流程分析了一下,很明顯沒有如期望的順序執行,我們先來回顧一下期望的順序是什么

// step 1
created() {
 // step 1.1
 let endTime;
 const startTime = Date.now();
 console.log(`start created: ${startTime}ms`);
 // step 1.2
 this.list = await this.getList();
 endTime = Date.now();
 console.log(this.list);
 console.log(`end created: ${endTime}ms, cost: ${endTime - startTime}ms`);
},
// step 2
mounted() {
 let endTime;
 const startTime = Date.now();
 console.log(`start mounted: ${startTime}ms`);
 console.log(this.list.rows);
 endTime = Date.now();
 console.log(`end mounted: ${endTime}ms, cost: ${endTime - startTime}ms`);
}

// step 1 => step 1.1 => step 1.2 => step 2

期望的打印結果是:

// step 1(created)
start created
// this.list
{__ob__: Observer}
end created
created cost: 3171.545166015625ms

// step 2(mounted)
start mounted
// this.list.rows
[{…}, __ob__: Observer]
end mounted
mounted cost: 2.88623046875ms

對比實際的打印和期望的打印,就知道問題出在created鉤子內使用了await的異步代碼,并沒有達到我們期望的那種的“異步代碼同步執行”的效果,僅僅是一定程度上達到了這個效果。

下面來分析一下為什么會出現這個非預期的結果!

在分析前,讓我們來回顧一下一些javascript的基礎知識!看看下面這段代碼:

(function __main() {
 console.log('start');
 setTimeout(() => {
 console.log('console in setTimeout');
 }, 0);
 console.log('end');
})()

// output
start
end
console in setTimeout

這個打印順序有沒有讓你想到什么?!

任務隊列!

vue中async-await如何使用

我們都知道JavaScript的代碼可以分成兩類:

同步代碼 和 異步代碼

同步代碼會在主線程按照編寫順序執行;

異步代碼的觸發過程(注意是觸發,比如異步請求的發起,就是在主線程同步觸發的)是同步的,但是異步代碼的實際處理邏輯(回調函數)則會在異步代碼有響應時將處理邏輯代碼推入任務隊列(也叫事件隊列),瀏覽器會在主線程(指當前執行環境的同步代碼)代碼執行完畢后以一定的周期檢測任務隊列,若有需要處理的任務,就會讓隊頭的任務出隊,推入主線程執行。

比如現在我們發起一個異步請求:

// exp-02
console.log('start');
axios.get('http://xxx.com/getList')
 .then((resp) => {
 console.log('handle response');
 })
 .catch((error) => {
 console.error(error);
 });
console.log('end');

在主線程中,大概首先會發生如下過程:

// exp-03
// step 1
console.log('start');

// step 2
axios.get('http://xxx.com/getList'); // 此時回調函數(即then內部的邏輯)還沒有被調用

// step 3
console.log('end');

在看看瀏覽器此時在干什么!

此時事件輪詢(Event Loop)登場,其實并非此時才登場,而是一直都在!

“事件輪詢”這個機制會以一定的周期檢測任務隊列有沒有可執行的任務(所謂任務其實就是callback),有即出隊執行。

當 step 2 的請求有響應了,異步請求的回調函數就會被添加到任務隊列(Task Queue)或者 稱為 事件隊列(Event Queue),然后等到事件輪詢的下一次檢測任務隊列,隊列里面任務就會依次出隊,進入主線程執行:即執行下面的代碼:

// 假定沒有出錯的話
((resp) => {
 console.log('handle response');
})()

到此,簡短科普了任務隊列的機制,聯想 exp-01 的代碼,大概知道出現非預期結果的原因了吧!

created鉤子中的await函數,雖然是在一定程度上是同步的,但是他還是被掛起了,實際的處理邏輯(this.list =resp.xxx)則在響應完成后才被添加進任務隊列,并且在主線程的同步代碼執行完畢后執行。 下面是將延時時間設為0后的打印:

start created
start mounted
undefined
end mounted
mounted cost: 2.88623046875ms
{__ob__: Observer}
end created
created cost: 9.76611328125ms

這側面說明了await函數確實被被掛起,回調被添加到任務隊列,在主線程代碼執行完畢后等待執行。

然后是為什么說 exp-01 的代碼是一定程度的同步呢?!

同步執行的另一個意思是不是就是:阻塞當前線程的繼續執行直到當前邏輯執行完畢~

看看 exp-01 的打印:

{__ob__: Observer}
end created
created cost: 3171.545166015625ms

end created 這句打印,是主線程的代碼,如果是一般的異步請求的話,這句打印應該是在 {__ob__: Observer} 這句打印之前的yo,至于為什么會這樣,這里就不多解析,自行google!

另外,這里來個小插曲,你應該注意到,我一直強調,回調函數被添加進任務隊列的時機是在響應完成之后,沒錯確實如此的!

但在不清除這個機制前,你大概會有兩種猜想:

1.在觸發異步代碼的時,處理邏輯就會被添加進任務隊列;
2.上面說到的,在異步代碼響應完成后,處理邏輯才會被添加進任務隊列;

其實大可推斷一下

隊列的數據結構特征是:先進先出(First in First out)

此時假如主線程中有兩個異步請求如下:

// exp-04
syncRequest01(callback01);
syncRequest02(callback02);

假設處理機制是第一點描述那樣,那么callback01就會先被添加進任務隊列,然后是callback02。

然后,我們再假設syncRequest01的響應時間是10s,syncRequest02的響應時間是5s。

到這里,有沒有察覺到違和感!

異步請求的實際表現是什么?是誰快誰的回調先被執行,對吧!那么實際表現就是callback02會先于callback01執行!

那么基于這個事實,再看看上面的假設(callback01會執行)~

ok!插曲完畢!

解法

首先讓我回顧一下目的,路由組件對異步請求返回的數據有強依賴,因此希望阻塞組件的渲染流程,待到異步請求響應完畢之后再執行。

這就是我們需要做的事情,需要強調的一點是: 我們對數據有強依賴 ,言外之意就是數據沒有按預期返回,就會導致之后的邏輯出現不可避免的異常。

接下來,我們就需要探討一下解決方案!

組件內路由守衛了解一下!?

beforeRouteEnter
beforeRouteUpdate (2.2 新增)
beforeRouteLeave

這里需要用到的路由守衛是: beforeRouterEnter , 先看代碼:

// exp-05
export default {
 beforeRouteEnter(to, from, next) {
 this.showLoading();
 this.getList()
  .then((resp) => {
  this.hideLoading();
  this.list = resp.data;
  next();
  })
  .catch((error) => {
  this.hideLoading();
  // handle error
  });
 },

 mounted() {
 let endTime;
 const startTime = Date.now();
 console.log(`start mounted: ${startTime}ms`);
 console.log(this.list.rows);
 endTime = Date.now();
 console.log(`end mounted: ${endTime}ms, cost: ${endTime - startTime}ms`);
 },
};

路由守衛 beforeRouterEnter ,觸發這個鉤子后,主線程都會阻塞,頁面會一直保持假死狀態,直到在調用 beforeRouterEnter 的回調函數 next ,才會跳轉路由進行新路由組件的渲染。

看起這個解決方案相當適合上面我們提出的需求,在調用 next 前,就可以去拉取數據!

但是如剛剛說到的,頁面在一直假死,加入數據獲取花費時間過長就難免變得很難看,用戶體驗未免太差

為此,在 exp-05 中我在請完成前后分別調用了 this.showLoading() 和 this.hideLoading() 以便頁面 keep-alive 。

這個處理假死的loading有沒有讓你想到寫什么,沒錯就是下面這個github跳轉頁面是頂部的小藍條

vue中async-await如何使用

想想就有點cool,當然還有很多的實現方式提升用戶體驗,比如作為body子元素的全屏loading,或者button-loading等等……

當然,我們知道阻塞主線程怎么都是阻塞了,loading只是一種自欺欺人式的優化(此時這個成語可不是什么貶義的詞語)!

因此,不是對數據有非常強的依賴,都應在路由的鉤子進行數據抓取,這樣就可以讓用戶“更快”地跳轉到目的頁。為避免頁面對數據依賴拋出的異常(大概就是 undefined of xxx ),我們可以對初始數據進行一些預設,比如 exp-01 中對 this.list.rows 的依賴,我們可以預設 this.list :

list: {
 rows: []
}

這樣就不會拋出異常,待到異步請求完成,基于vue的update機制二次渲染我們的預期數據~

小結

對于 exp-01 的寫法,也不能說他是錯誤或不好的寫法,凡事都要看我們是出于什么目的,如果僅僅是為了保證多個異步函數的執行順序, exp-01 的寫法沒有任何錯誤,因此async/await不能用在路由鉤子上什么的并不存在!

上述內容就是vue中async-await如何使用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

鞍山市| 谷城县| 青岛市| 社会| 柞水县| 托克托县| 九龙城区| 九寨沟县| 枣阳市| 磐石市| 杭锦旗| 荔浦县| 武义县| 乌什县| 郸城县| 三江| 北海市| 青冈县| 垦利县| 阿巴嘎旗| 伊金霍洛旗| 古田县| 白玉县| 和林格尔县| 邵东县| 谢通门县| 康马县| 枣强县| 洪泽县| 睢宁县| 孝昌县| 启东市| 元氏县| 镇雄县| 蚌埠市| 交口县| 凭祥市| 涿鹿县| 锦屏县| 阿拉善左旗| 昭平县|