[JavaScript] Promise 與 Ajax/Axios

updateTime: 2019-4-5 23:00 javascript

updateContent: async在項目中的使用簡談vue

前言

在單線程的js執行中,必然須要異步的出現來協程操做,不然就不用玩了, 而在js異步編程方案中,咱們經歷了回調地獄後終於推出了更合理強大的新方案,那就是——Promise,而在經歷了co模塊的洗禮後,es7順利推出了Generator 的語法糖——Async(誰用誰知道,真滴爽=-=)java

Promise 承諾

What

用於處理異步回調的一種解決方案,比傳統回調更強大合理

WhyThisName

根據promise對象的第一特徵,除了異步操做能夠影響其狀態,其餘外界操做都不能影響,表示一種承諾,即其餘手段沒法改變。ios

兩個特徵:

1. 對象狀態不受外界影響,三種狀態爲: pengding, fulfilled(已成功), rejected(已失敗)
2. 狀態改變後不在改變,只有兩種改變: pending->fulfilled / pending->rejected

三個缺點: 

1. new後沒法取消
2. 無回調則內部錯誤沒法拋出

3. 當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)
es6

How to use 

const promise = new Promise((resolve, reject) => {
  if (異步操做完畢) {        
      resolve(val)      
  } else {       
      reject(error)      
  }
 })  
 promise.then((val) => { 
     // doSuccess 
 }, (err) => {      // doFailure })複製代碼


基於Promise的Ajax,告別回調地獄

手打一時爽..ajax

// 手寫一個ajax by promise 格式崩潰了見諒..
function _ajaxGet (url, params) {
      const promise =  new Promise(function(resolve, reject){
         const handle = function () {
             if (this.readyState == 4) {
                 if (this.status === 200 || this.status === 304) {
                     resolve(this.response)                                                             
                 } else {
                    reject(new Error(this.statusText))                                          
                 }                
             }                            
         }            
         const XHR = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP')
         params = params || ''
         XHR.open('GET', url + params, true)
         XHR.onreadystatechange = handle
         XHR.responseType = 'json'
         XHR.setRequestHeader('Accept', 'application/json')
         XHR.send()        
     })        
     return promise
}    
function _ajaxPost (url, data) {
        const promise =  new Promise(function(resolve, reject){
            const handle = function () {
                if (this.readyState == 4) {
                    if (this.status === 200 || this.status === 304) {
                        resolve(this.response)
                                                                  
} else {
                        
reject(new Error(this.statusText))
                                            
}                
}                            
}            
const XHR = XMLHttpRequest ? new XMLHttpRequest() : new window.ActiveXObject('Microsoft.XMLHTTP')
XHR.open('POST', url, true)            
XHR.onreadystatechange = handle            
XHR.responseType = 'json'            
XHR.setRequestHeader('Content-Type', '"application/json')            
XHR.send(data)                    
})        
return promise    
}    
_ajaxGet('test.json').then(res => {
        console.log(res)
}).catch(err => {
        console.log(err)
})    
_ajaxPost('test.json', {id: 123})複製代碼


其實把上面的代碼封裝封裝加一些其餘的配置參數,就是一個簡單的axios了(?我本身覺得的),不過axios的原理就是經過promise實現的就沒錯了。編程

能夠發現區別於傳統的回調裏面寫異步,回調一層又一層,真的要好不少。json

快速認識Promise對象的一些方法

1. Promise.all([p1,p2,p3])axios

相似於串聯電路,數組

數組中的全部promise實例狀態都爲已定型(resolve)時,則返回一個全部實例的返回值數組進行該promise以後的回調,

如有一個/多個爲reject則返回第一個reject的promise的error

demo(來自es6阮老師書中demo)

// 生成一個Promise對象的數組
const promises = [2, 3, 5, 7, 11, 13].map(function (id) {
  return getJSON('/post/' + id + ".json");
});

Promise.all(promises).then(function (posts) {
  // ...
}).catch(function(reason){
  // ...
});複製代碼

2. Promise.race([p1,p2,p3])

相似於並聯電路,

返回多個實例中第一個進行了狀態變換的實例的返回值做爲回調函數的參數

3. 後續補充...

Promise內部一探

雖然沒看源碼,但猜想是用觀察者模式實現的,經過改變狀態值來觸發對應回調函數(根據其特性加一些別的參數設置),後面出一期觀察者模式與promise。

一塊兒來實現一個Promise

Promise的缺陷

假設有這樣一個場景,五個動畫,順序加載到一個dom上,要求五秒內完成,不然提示報錯並關閉動畫。好了這個場景先不去實現,可想而知,爲了實現第一個順序加載,你須要

.then(res => {//anim1(); resolve()})

.then(res => {//anim2(); resolve()})

.then(res => {//anim3(); resolve()})

.then(res => {//anim4(); resolve()})

.then(res => {//anim5()})

代碼越寫越長,橫縱變胖,賊難看,而且不容易察看

這個時候

Async Await(Generaor yiled)閃亮登場

今天先寫到這。。。累,明天接着寫

下面是目錄

Generator 與 執行器 與 Thunk(攜程函數)

Generator函數 function* helloworld () {}  利用Generator能夠實現js的狀態機

是否是很像c中的指針, 沒錯Generator函數調用後不會執行(交出函數執行權),可是會返回一個遍歷器對象,經過對該對象的next(繼續)方法調用,對函數內代碼進行執行,每次next執行會在遇到的第一個yield(暫停)停下,而後返回一個鍵值分別爲value,done的對象來表示當前暫停右側表達式的值和遍歷的狀態。在多任務狀況下,多個Generator(async:我是誰我在那)經過yield(await:我是誰我在哪,而且我遇到的不是異步我會當即執行再返回一個已定型的promise對象)來交換控制權。

一直執行繼續(next)而後當遍歷狀態(done)爲true時遍歷結束,下次再執行就會一直返回

{value: undefined, done: true}

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator();
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }
複製代碼

代碼終於看起來比回調清晰了,可是流程卻有些不如意,那麼如何自動執行呢,有請

Thunk 傳名調用的一種實現策略

what: 計算機語言求值策略中的傳名調用,即將表達式直接傳入,減小沒必要要計算損失。編譯器的「傳名調用」實現,每每是將參數放到一個臨時函數之中,再將這個臨時函數傳入函數體。這個臨時函數就叫作 Thunk 函數。

// 正常版本的readFile(多參數版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(單參數版本)
var Thunk = function (fileName) {
  return function (callback) {
    return fs.readFile(fileName, callback);
  };
};

var readFileThunk = Thunk(fileName); 複製代碼

readFileThunk(callback); 

固然js是值傳遞調用!在js中Thunk起到的主要做用是多參變單參後將回調傳回。上面看到將callback傳回。

而在這裏對Genrator的自動流程管理實現的幫助中Thunk起到的是對next指針傳出後再傳入上次yield表達式執行完畢地址,這樣就能夠能夠把執行權在next傳出後又從新傳入,拿回執行權。

一句話,上次異步成功後自動繼續yield後代碼。

可是看起來好麻煩,還不如寫寫thenthenthen(Promise自動執行,變胖就變胖,口亨)是吧,還要寫Genrator的執行器。這個時候Co閃亮登場。

Co 小輕美

var co = require('co');
co(gen);
// Generator函數傳入co中自動執行複製代碼

what: Thunk與Promise的結合糖

tip: 注意了co的實參只能是Thunk 函數或 Promise 對象。支持併發。

Async Await的優勢

  • 內置執行器
  • 更好的語義
  • 更廣的適用性
  • 返回值是 Promise

實例Demo與項目中應用簡談

最近使用vue作了一個項目相似於阿里的iconFont,首先用一個很簡單的首頁狀態邏輯來介紹async的使用(順序加載)

1. 當進入首頁組件前在app組件created中首先調用initUserInfo異步函數,該函數在store的index.js的dispatch中定義

2. 而後咱們須要在首頁組件加載時調用幾個請求來渲染對應用戶的數據,這幾個請求須要使用userid這個數據

若是用promise來實現的話,那就是如下代碼

methods: {
initUserInfo () {
    return _axios.get(uri).then(res => { // 參考上面用promise實現的ajax.getJson
        commit('userInfo', res.data.data) // 全局狀態更新處理 
    })
}
initMyproInfo () {
    // ...
    return _axios.post(getProUri, {usrId: this.userInfo.id}) // ...mapGetters(['usrInfo'])
}
,
created () {
     this.initUserInfo().then(res => {
        console.log(res) // 打印獲取到的用戶數據
        return this.initMyproInfo()
    }).then(res => { // 這個回調依賴於調用他的promise對象狀態
        this.this._message.info(`data id is ${data.id}`)
    }).catch(err => { // 捕捉then中第一個錯誤
        console.log(err)
    }) 
}
複製代碼

若是用async 來實現的話

//...mapActions(['initUserInfo'])
async initUserInfo ({commit}) {
    await let {data} = _axios.get(uri)
    commit('initUserInfo', data.data) // 全局狀態更新處理
    return data.data            
}
async created () { // 是否是看起來清爽多了!
    await this.initUserInfo()
    let data = await this.initMyproInfo() // 正常結果下,await後是一個promise對象,返回該對象的結果,在本例中即返回pro請求後的結果res
    this._message.info(`data id is ${data.id}`)
}複製代碼




Last, 各位若是以爲還喜歡,對你理解有用,麻煩點個贊吧(給您鼓掌了!^_^)

若是有錯誤的地方,請您不吝指出,謝謝!

相關文章
相關標籤/搜索