梳理全部前端異步解決方案

衆所周知js是單線程,從頭跑到尾而後不停的事件循環,可是瀏覽器是多線程的,全部前端全部的異步能夠概括爲,js的事件循環在根據標準不停的先後執行不一樣異步線程的回調前端

其實如今js對於異步的全部解決方案不論是async 仍是 promise 仍是監聽什麼的歸根結底都是回調觸發,而咱們至今爲異步所做的全部努力不過是讓異步回調寫的更加像同步一些 目前前端對異步的處理大體通過這三個階段ios

1 縱向無限回調,好比ajax的回調ajax

2 promise 經過then鏈式回調,好比axios等axios

3asyns 經過promise在resolve中交換控制權來自動執行的迭代器來讓異步變得看起來像同步api

無限回調就不講了,每一個回調函數以前相互依賴造成強耦合從而使代碼縱向發展變成一坨代碼數組

直接從promise開始,promise其實就是回調換了一種寫法而已,也就是對回調多了一層封裝 簡單來說就是他是一個原生提供的js對象,接受一個函數做爲參數來生成實例,這個函數又以resolve,reject這兩個函數做爲參數promise

一共有三種狀態分別是進行中(pending)已解決(fulfilled)已失敗(rejected)瀏覽器

而後咱們日常說的什麼把一個異步回調封裝成promise什麼的聽起來高大上不過就是,你本身根據回調函數的結果,自行選擇執行resolve(將狀態從pending轉換成fulfilled)或者reject(將狀態從pending轉換成rejected)bash

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操做成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
複製代碼

而後他有一個最重要也是用的最多的一個方法**then()**他接受兩個函數做爲參數分別做爲上面說的resolve,reject執行完成後的回調參數,而參數則是則是由resolve傳入數據結構

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操做成功 */){
  console.log('結果:')
    resolve('123');
  } else {
    reject('321');
  }
});
promise.then(x=>{
    console.log(x)
},y=>{
    console.log(y)
}
)

//結果:
123
321
複製代碼

同時then()也是返回一個promise對象因此能夠繼續鏈式回調,上一個then()方法return的參數能夠做爲下一個then()方法的參數 除此以外還有all(),race()之類的輔助方法,都是些顧名思義的api

其實從本質上來講promise就是回調換了一種寫法,在回調發生之時去修改promise的內部狀態,而後經過不一樣的狀態,來去決定執行then()裏面的哪一個方法,當業務邏輯複雜起來的時候,你特麼不照樣不停的then(),也很醜啊!

因此出現了async 和 await 寫起來感受和同步差很少,又美觀又好看,媽媽不再用擔憂我代碼醜了,雖然看起來高大上其實本質仍是回調,在我看來最大的好處不過是開發體驗的提高而已

首先要明白的是 async 不過是 Generator的語法糖,而Generator本質是個不能自動執行的迭代器,那麼問題來了什麼是js的迭代器呢?

迭代器本質是來講是一個指針對象指向數據結構的起始位置,經過調用next()方法使其指向下一個存儲單元,且訪問該數據的描述信息

像在js中[]就原生裝載了遍歷器接口,訪問數組實例的[Symbol.iterator]屬性,便會返回該數據結構的遍歷器對象,而後調用這個對象的next()方法便會依次返回該數據機構各個位置的描述信息

let a = [1,2]
 let i = a[Symbol.iterator]();
 console.log(i.next())
 
 返回:{value: 1, done: false}
複製代碼

Generator 函數,即是返回這麼一個遍歷器對象,經過調用.next()方法來返回他內部保存的狀態,因此你也能夠叫他狀態保存機

function* generator(){
		   yield 1;
		   yield 2;
		   return 3;
		}
		let g = generator()
		g.next()
		
		返回  { value: 1, done: false }...
複製代碼

他之因此能被用在異步應用中的一個關鍵屬性是:你能夠經過next()傳值來做爲上一步操做所產生的結果值,這樣咱們就能夠經過外部輸入的參數值不一樣來對Generator的執行進行干預,從而達到交換執行權的效果

function* generator(){
		   yield 1;
		   let x = yield 2;
		   console.log(x)
		   return 3;
		}
		let g = generator()
		g.next(5)
		
		返回  { value: 1, done: false },5
複製代碼

那咱們要作的就是讓Genrator自動執行,一直到最後一個狀態,好比這樣:

function* generator(){
		   yield 1;
		   yield 2;
		   return 3;
		}
		let g = generator()
		if(!g.next().done){
		    g.next()
		}
複製代碼

可是這樣作不到咱們想要的異步操做,咱們想要的效果是,在上一步異步操做執行成功的後執行下一步異步操做,那麼咱們的思路就是提供一個方法,讓他能夠自動的執行generator,在每個異步操做經過yield語句將函數的執行權過分給異步方法,同時generator外部咱們能夠經過獲取到異步函數的返回結果來決定是否啓用下一步,若是異步執行成功,咱們再將函數的執行權交還給generate方法。以前咱們說過promise,那麼若是咱們把每個異步操做都封裝成promise對象,這樣咱們就能夠嘗試着寫一個自動執行的方法

function run(gen){
  var g = gen();

  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }

  next();
}

run(gen);
複製代碼

由於yield返回的是一個Promise對象,那麼咱們在調用next()方法時將獲取到這個對象,以前咱們說過promise的then方法接受兩個方法做爲參數分別做爲執行成功和執行失敗的回調函數,那麼咱們就只須要在執行成功的那個方法也就是第一個傳入的函數執行next()方法那麼若是每一步異步操做都正常執行的話那麼整個generator方法將自動執行,async機制就是自帶了一個能夠判斷promise狀態的執行器,能夠在await的時候將resolve的值給return 回來

相關文章
相關標籤/搜索