js 異步解決方案

js的異步請求歷來被詬病,但是社區和規範一直也在努力,這裏簡單說下這些變化。

ajax

嚴格地說ajax屬於與服務器交換數據的API,與異步並不完全相同。但對於早期的前端來說,異步的操作基本都是與ajax交涉的過程。

2005年ajax-new-approach-web-application一文催生了ajax技術,隨後各瀏覽器紛紛實現了XmlHttpRequest。其具有劃時代的意義,爲網頁性能和用戶體驗帶來了巨大的提升。下面是一個基於XmlHttpRequest對象實現的簡單取數據的函數:

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function(){if(xhr.readyState === 4){
    if(xhr.status == 200){
    console.log(xhr.responseText)
    }
}}
xhr.open('GET','https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState',true)
xhr.send(null)

可以看出這個對象具有濃濃的面向對象的風格,沒有函數式編輯的優雅。目前作爲XHR的替代api——fetch,則改正了這一股風氣。

jquery deffered

這玩意已經退出歷史舞臺了,我差不多已經忘記了。但是17年去頭條面試還居然問我$deffered是怎麼實現的,要寫代碼。我表示很崩潰。

promise

promise 是es6的規範,它的規則如下:
promise的參數是一個函數,這個函數執行時會被注入兩個參數,resolve和reject,這兩個函數會改變promise對象的狀態,狀態發生變化時則會執行相應的回調
具體有三個狀態:

  • pending
  • fullfilled
  • rejected

狀態轉換規則如下:

所以對於一個連續三次的異步操作,它的代碼可能如下:

function resolveNumAfter2S(x){
    return new Promise(resolve=>{
        setTimeout(()=>{resolve(x)}, 500)
    })
}

resolveNumAfter2S(10)
.then(x => {return resolveNumAfter2S(x+1)})
.then(x => {return resolveNumAfter2S(x+2)})
.then(x => {console.log(x)})

promise 隱藏了中間一些抽象的處理,resolve,reject導致的狀態變化以及它們的實現,then,catch的綁定都隱藏了,所以理解起來會有些困難,如果自己用js寫一個類似的實現,則能容易理解它的功能,下面是一個小意思:

const MyPromise = function(f){
    f.bind(this, this._resolve.bind(this), this._reject.bind(this))()
}

MyPromise.prototype = Object.assign(MyPromise.prototype, {
    status: 'pending',
    _resolve: function (data) {
        this.status = 'fullfilled';
        this._then(data);
    },
    _reject: function (err) {
        this.status = 'rejected';
        this._catch(err);
    },
    then: function (f) {
        this._then = f;
        return this;
    },
    catch: function (f) {
        this._catch = f;
        return this;
    }
})


function resolveAfter2s(x){
    return new MyPromise((reslove,reject)=>{
        setTimeout(()=>{
            reject("err")
        }, 2000)
    })
}

var res = resolveAfter2s(10).then(x=>{
    console.log(x)
}).catch(err => {
    console.log(err)
})

async await

作爲es7的終級異步解決方案,async/await的規則如下:

  • 使用async前綴定義的函數,會返回一個promise對象
  • await只能在async function 中使用
  • 遇到await 指令,異步代碼的執行和同步代碼一樣,不會跳過

對於一個簡單的例子,大約很難體會它到底有什麼用, 比如:

function resolveNumAfter2S(x){
    return new Promise(resolve=>{
        setTimeout(()=>{resolve(x)}, 2000)
    })
}

async function getNum(){
    return await resolveNumAfter2S(1)
}

getNum().then(x=>{
    console.log(x)
})

但考慮以下場景,就會發現其方便之處:

function resolveNumAfter2S(x){
    return new Promise(resolve=>{
        setTimeout(()=>{resolve(x)}, 500)
    })
}
// 假設你有一個需求,需要請求三個異步的數據,而每一步都要依賴上一步的數據,你完全可以以同步的代碼體驗去寫代碼:
async function getNum(){
    let a = await resolveNumAfter2S(6);
    let b = await resolveNumAfter2S(a);
    let c = await resolveNumAfter2S(b+a);

    return a+b+c;
}

getNum().then(x=>{
    console.log(x)
})
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章