成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

再談 JavaScript 異步編程

RobinQu / 2653人閱讀

摘要:隨著前端的發(fā)展,異步這個詞真是越來越常見了。真正帶來革命性改變的是規(guī)范。借助,我們可以這樣完成異步任務(wù)好棒寫起來像同步處理的函數(shù)一樣別著急,少年??偨Y(jié)以上就是筆者總結(jié)的幾種異步編程模式。

隨著前端的發(fā)展,異步這個詞真是越來越常見了。假設(shè)我們現(xiàn)在有這么一個異步任務(wù):

  

向服務(wù)器發(fā)起數(shù)次請求,每次請求的結(jié)果作為下次請求的參數(shù)。

來看看我們都有哪些處理方法:

Callbacks

最先想到也是最常用的便是回調(diào)函數(shù)了,我們來進(jìn)行簡單的封裝:

javascriptlet makeAjaxCall = (url, cb) => {
    // do some ajax
    // callback with result
}

makeAjaxCall("http://url1", (result) => {
    result = JSON.parse(result)
})

嗯,看起來還不錯!但是當(dāng)我們嘗試嵌套多個任務(wù)時,代碼看起來會是這樣的:

javascriptmakeAjaxCall("http://url1", (result) => {
    result = JSON.parse(result)

    makeAjaxCall(`http://url2?q=${result.query}`, (result) => {
        result = JSON.parse(result)

        makeAjaxCall(`http://url3?q=${result.query}`, (result) => {
            // ...
        })
    })
})

天哪!快讓那堆 }) 見鬼去吧!

于是,我們想嘗試借助 JavaScript 事件模型:

Pub/Sub

在 DOM 事件的處理中,Pub/Sub 是一種很常見的機(jī)制,比如我們要為元素加上事件監(jiān)聽:

javascriptelem.addEventListener(type, (evt) => {
    // handler
})

所以我們是不是也可以構(gòu)造一個類似的模型來處理異步任務(wù)呢?

首先是要構(gòu)建一個分發(fā)中心,并添加 on / emit 方法:

javascriptlet PubSub = {
    events: {},
    on(type, handler) {
        let events = this.events
        events[type] = events[type] || []
        events[type].push(handler)
    },
    emit(type, ...datas) {
        let events = this.events

        if (!events[type]) {
            return
        }

        events[type].forEach((handler) => handler(...datas))
    }
}

然后我們便可以這樣使用:

javascriptconst urls = [
    "http://url1",
    "http://url2",
    "http://url3"
]

let makeAjaxCall = (url) => {
    // do some ajax
    PubSub.emit("ajaxEnd", result)
}

let subscribe = (urls) => {
    let index = 0

    PubSub.on("ajaxEnd", (result) => {
        result = JSON.parse(result)

        if (urls[++index]) {
            makeAjaxCall(`${urls[index]}?q=${result.query}`)
        }
    })

    makeAjaxCall(urls[0])
}

嗯……比起回調(diào)函數(shù)好像沒有什么革命性的改變,但是這樣做的好處是:我們可以將請求和處理函數(shù)放在不同的模塊中,減少耦合。

Promise

真正帶來革命性改變的是 Promise 規(guī)范[1]。借助 Promise,我們可以這樣完成異步任務(wù):

javascriptlet makeAjaxCall = (url) => {
    return new Promise((resolve, reject) => {
        // do some ajax
        resolve(result)
    })
}

makeAjaxCall("http://url1")
    .then(JSON.parse)
    .then((result) => makeAjaxCall(`http://url2?q=${result.query}`))
    .then(JSON.parse)
    .then((result) => makeAjaxCall(`http://url3?q=${result.query}`))

好棒!寫起來像同步處理的函數(shù)一樣!

別著急,少年。我們還有更棒的:

Generators

ES6 的另外一個大殺器便是 Generators[2]。在一個 generator function 中,我們可以通過 yield 語句來中斷函數(shù)的執(zhí)行,并在函數(shù)外部通過 next 方法來迭代語句,更重要的是我們可以通過 next 方法向函數(shù)內(nèi)部注入數(shù)據(jù),動態(tài)改變函數(shù)的行為。比如:

javascriptfunction* gen() {
    let a = yield 1
    let b = yield a * 2
    return b
}

let it = gen()

it.next() // output: {value: 1, done: false}
it.next(10) // a = 10, output: {value: 20, done: false}
it.next(100) // b = 100, output: {value: 100, done: true}

通過 generator 將我們之前的 makeAjaxCall 函數(shù)進(jìn)行封裝:

javascriptlet makeAjaxCall = (url) => {
    // do some ajax
    iterator.next(result)
}

function* requests() {
    let result = yield makeAjaxCall("http://url1")
    result = JSON.parse(result)
    result = yield makeAjaxCall(`http://url2?q=${result.query}`)
    result = JSON.parse(result)
    result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

let iterator = requests()
iterator.next() // get everything start

哦!看起來邏輯很清楚的樣子,但是每次都得從外部注入 iterator 感覺好不舒服……

別急,我們讓 PromiseGenerator 混合一下,看會產(chǎn)出什么黑魔法:

javascriptlet makeAjaxCall = (url) => {
    return new Promise((resolve, reject) => {
        // do some ajax
        resolve(result)
    })
}

let runGen = (gen) => {  
    let it = gen()

    let continuer = (value, err) => {
        let ret

        try {
            ret = err ? it.throw(err) : it.next(value)
        } catch (e) {
            return Promise.reject(e)
        }

        if (ret.done) {
            return ret.value
        }

        return Promise
            .resolve(ret.value)
            .then(continuer)
            .catch((e) => continuer(null, e))
    }

    return continuer()
}

function* requests() {
    let result = yield makeAjaxCall("http://url1")
    result = JSON.parse(result)
    result = yield makeAjaxCall(`http://url2?q=${result.query}`)
    result = JSON.parse(result)
    result = yield makeAjaxCall(`http://url3?q=${result.query}`)
}

runGen(requests)

runGen 函數(shù)看起來像個自動機(jī)一樣,好厲害!

實際上,這個 runGen 的方法是對 ECMAScript 7 async function 的一個實現(xiàn):

async function

ES7 中,引入了一個更自然的特性 async function[3]。利用 async function 我們可以這樣完成任務(wù):

javascriptlet makeAjaxCall = (url) => {
    return new Promise((resolve, reject) => {
        // do some ajax
        resolve(result)
    })
}

;(async () => {
    let result = await makeAjaxCall("http://url1")
    result = JSON.parse(result)
    result = await makeAjaxCall(`http://url2?q=${result.query}`)
    result = JSON.parse(result)
    result = await makeAjaxCall(`http://url3?q=${result.query}`)
})()

就像我們在上文把 PromiseGenerator 結(jié)合在一起時一樣,await 關(guān)鍵字后同樣接受一個 Promise。在 async function 中,只有在 await 后的語句完成后剩下的語句才會被執(zhí)行,整個過程就像我們用 runGen 函數(shù)封裝 Generator 一樣。

總結(jié)

以上就是筆者總結(jié)的幾種 JavaScript 異步編程模式。在行文過程中,我們只是簡單描述了這幾種模式,并沒有提及錯誤處理的過程,您要是對此感興趣,可以參考下文列出的引用文章。

(全文完)

參考資料

Promises/A+ Specification

Going Async With ES6 Generators

ES7 async functions

Simplifying Asynchronous Coding with ES7 Async Functions

從第三方實現(xiàn)看 Promise

  

重編自我的博客,原文地址:https://idiotwu.me/going-async-with-javascript/

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85736.html

相關(guān)文章

  • 再談JavaScript模塊化

    摘要:應(yīng)用日益復(fù)雜,模塊化已經(jīng)成為一個迫切需求。異步模塊加載機(jī)制。引用的資源列表太長,懶得回調(diào)函數(shù)中寫一一對應(yīng)的相關(guān)參數(shù)假定這里引用的資源有數(shù)十個,回調(diào)函數(shù)的參數(shù)必定非常多這就是傳說中的 簡述 緣起 模塊通常是指編程語言所提供的代碼組織機(jī)制,利用此機(jī)制可將程序拆解為獨立且通用的代碼單元。 模塊化主要是解決代碼分割、作用域隔離、模塊之間的依賴管理以及發(fā)布到生產(chǎn)環(huán)境時的自動化打包與處理等多個方面...

    MorePainMoreGain 評論0 收藏0
  • JS筆記

    摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。異步編程入門的全稱是前端經(jīng)典面試題從輸入到頁面加載發(fā)生了什么這是一篇開發(fā)的科普類文章,涉及到優(yōu)化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識之 HTTP 協(xié)議 詳細(xì)介紹 HTT...

    rottengeek 評論0 收藏0
  • 從setTimeout-setInterval看JS線程

    摘要:提出標(biāo)準(zhǔn),允許腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作。所以,這個新標(biāo)準(zhǔn)并沒有改變單線程的本質(zhì)。事件循環(huán)主線程線程只會做一件事,就是從消息隊列里面取消息執(zhí)行消息,再取消息再執(zhí)行。工作線程是生產(chǎn)者,主線程是消費者。 最近項目中遇到了一個場景,其實很常見,就是定時獲取接口刷新數(shù)據(jù)。那么問題來了,假設(shè)我設(shè)置的定時時間為1s,而數(shù)據(jù)接口返回大于1s,應(yīng)該用同步阻塞還是異步?我們...

    elliott_hu 評論0 收藏0
  • js的單線程,異步及回調(diào)函數(shù)

    摘要:當(dāng)主線程開始執(zhí)行異步任務(wù),實際就是執(zhí)行對應(yīng)的回調(diào)函數(shù)。異步任務(wù)必須指定回調(diào)函數(shù)。所以注意的是,只是將事件插入了任務(wù)隊列,必須等到當(dāng)前代碼執(zhí)行棧執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)。 最近本人對于js的運行機(jī)制,特別是異步,還有回調(diào)函數(shù)感覺很亂,于是參考了很多有用的博客(博客原文地址會在文末給出),整理如下: js單線程 我們都知道,Javascript語言的執(zhí)行環(huán)境是單線程(si...

    Songlcy 評論0 收藏0
  • JavaScript執(zhí)行機(jī)制、事件循環(huán)

    摘要:曾經(jīng)的理解首先,是單線程語言,也就意味著同一個時間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時有兩個線程,一個線程在某個節(jié)點上編輯了內(nèi)容,而另一個線程刪除了這個節(jié)點,這時瀏覽器就很懵逼了,到底以執(zhí)行哪個操作呢所以,設(shè)計者把 Event Loop曾經(jīng)的理解 首先,JS是單線程語言,也就意味著同一個時間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...

    rose 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<