前端幾種異步的處理方式

如果是單個ajax請求,不帶有接口錢數據相互依賴的,其實怎么請求都沒關系.

jQuery,axios,vue-resource,XMLHttpRequest

但一點有了接口間數據的相互依賴,那么問題就來了.

由于,每一個請求都是獨立的,且回調的時機不確定,為了保證請求接口數據返回的過程可控和相互依賴關系,可能需要費點周折.


1. ajax callback

$.get('url',function(data){
    $.get('url2' + data.id,function(data2){
        $.get('url3' + data2.id,func(data){
            // 拿到最終數據
        })
    })
})

使用最原始的方案,ajax callback 這種 回調函數嵌套的方式,在請求數據上面是沒有問題的.在一個接口請求完畢之后,拿到數據,在接著請求下一個接口,很好理解,也很好書寫.

問題在于:

當接口依賴多了之后,整個代碼的嵌套層級就會變多,代碼應該的豎向發(fā)展,變成了橫向.
這樣可能會導致代碼結構不清晰,不便于后期的維護.


2. Promise

function pget(url) { 
    return new Promise(function(reslove,reject){
        $.get(url,function(data){
            if (data.err) reject(data.err)
            resolve(data)
        })
    })
}


pget('url')
    .then(res=>{
        return pget('url2' + res.id)
    })
    .then(res=>{
        return pget('url3' + res.id)
    })
    .then(res=>{
        // 拿到最終數據
    })

使用 then 確實能把callback 那種橫向的趨勢變成更加符合代碼風格的縱向.

問題在于:

then 太多了,且語義化不強,前面的then也許還知道是干什么的,到了后面可能就懵逼了.


3. Generator


function *gen() {
    let data1 = yield $.ajax('url')
    let data2 = yield $.ajax('url2' + data1.id)
    yield $.ajax('url3') // 注意,在jQuery v3.0+版本,$.ajax() 支持了 promise
}

let g = gen()
g.next().value
    .then(res=>{
        g.next(res.id).value
            .then(res=>{
                g.next(res.id).value
                    .then(res=>{
                        // 拿到數據了,該干嘛干嘛.
                    })
            })
    })

問題在于

  • 相比 $.ajax() 函數嵌套,變的更加復雜。
  • 相比 promise 屬性變的更多。(.next().value)

三種方式.

  1. 第一種 ajax callback 存在回調函數,結構是橫向發(fā)展的.
  2. promise 確實結構變清晰了,在每一次的 reslove 返回一個新的 promise ,把這種接口依賴的操作用 then 縱向連接了起來.
  3. generator 就有點扯淡了,不光需要next() 還要 .value,更加扯淡的是,嵌套層級和 $.ajax() 沒有區(qū)別,甚至于比 $.ajax() 寫的代碼更多了.

我們所希望的

不管是 $.ajax() callback 還是 promise then 或者是 generator .next().

我們都希望都以同步的方式去寫異步代碼.

let data = 異步請求數據(url)
let data2 = 異步請求數據(url2 + data.id)
let data3 = 異步請求數據(url3 + data2.id)

console.log(data,data1,data2)

由于 generator可以一次一次的拿到 promise,并且可以暫停執(zhí)行.

可以根據 generator 的這個特性來實現以同步代碼的寫法來執(zhí)行異步任務

function readFilePromise(path) {
  return new Promise(function (reslove, reject) {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err)
      reslove(data)
    })
  })
}


function* gen2() {
  let data1 = yield readFilePromise('./1.txt')
  console.log(`data1:${data1}`)
  let data2 = yield readFilePromise('./2.txt')
  console.log(`data2:${data2}`)
}

此段代碼,正常情況下,每一次 next() 拿到了的對象 .value 都是一個 promise.

核心思想是,我們等待 promse 對象,執(zhí)行完畢了,在繼續(xù)執(zhí)行下一個 yield .

function genRunner(gen) {
    let g = gen() // 拿到迭代器指針
    function next(data) {
        let nextObj = g.next(data)
        if (!nextObj.done) { // 如果迭代沒有完成
            // 等待當前這個 promise執(zhí)行完成了.
            nextObj.value.then(res=>{
            // 繼續(xù)下一次的迭代.并把結果傳遞給當前的next()方法.
              next(res)  
            })
        }
    }
    
    next() // 第一此調用第一個 yield ,第一個yield沒辦法接受參數.
}

測試一下:

genRunner(gen2)

結果:

data1:1111111111
data2:22222222222

所以,我們可以利用 generator & runner 的工具,在 generator 中像寫同步代碼那樣書寫異步代碼.

genRunner 函數到底干了些什么事情?

  • 首先,generator 我們是可以手動調用 next() 一步步執(zhí)行的.
  • genRunner 首選拿到 迭代器指針,指向第一個 next() 返回的obj
  • 根據這個obj.done判斷迭代器是否遍歷完畢.
    • 如果沒有遍歷完畢,

      • 就先拿到這個obj.value ,也就是 promise 對象,執(zhí)行它的then.
      • 等待這個 promisethen 執(zhí)行完畢之后,再次手動調用 next()
    • 如果執(zhí)行完畢了,就什么也不做.


常用的工具還有 co

npm i co
co(gen2)
data1:1111111111
data2:2222222222

問題在于

如果我們非要使用 generator 發(fā)送異步請求的話,那么為了不寫惡心的嵌套代碼,就需要借助第三方 co,runner 這樣的插件.(或者自己寫一個)


使用 ES7 推出的 async / await

function readFilePromise(path) {
  return new Promise(function (reslove, reject) {
    fs.readFile(path, 'utf8', (err, data) => {
      if (err) reject(err)
      reslove(data)
    })
  })
}


async function readFile() {
    let res1 = await readFilePromise('./1.txt')
    let res2 = await readFilePromise('./2.txt')
}

結果:

data1:1111111111
data2:2222222222

async & await 基本是個語法糖,它結合了 generator & 類似 runner | co 的功能,能讓我們很舒服的用同步代碼的方式寫異步代碼.

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 異步編程對JavaScript語言太重要。Javascript語言的執(zhí)行環(huán)境是“單線程”的,如果沒有異步編程,根本...
    呼呼哥閱讀 7,400評論 5 22
  • 弄懂js異步 講異步之前,我們必須掌握一個基礎知識-event-loop。 我們知道JavaScript的一大特點...
    DCbryant閱讀 2,876評論 0 5
  • 一、Javascript實現異步編程的過程以及原理 1、為什么要用Javascript異步編程 眾所周知,Java...
    Ebony_7c03閱讀 932評論 0 2
  • Prepending(進行時),Resolve(成功了),Reject(失敗了),then......等 1.Pr...
    _菩提本無樹_閱讀 49,376評論 0 21
  • 一棵樹愛上了馬路對面另一顆樹。然后就沒有然后了,有些事,一開始就是結束。
    lnhy閱讀 142評論 0 0

友情鏈接更多精彩內容