亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

異步處理方案系列- 1.callback

anRui / 3052人閱讀

摘要:一個(gè)沒(méi)有返回值的函數(shù)執(zhí)行的效果其實(shí)是利用它的副作用一個(gè)沒(méi)有返回值和利用副作用的函數(shù)其實(shí)就是一個(gè)黑洞。

本篇博客尚未上傳 github

github 首頁(yè)(star+watch,一手動(dòng)態(tài)直達(dá)): https://github.com/HCThink/h-blog

掘金 link , 掘金 專欄

segmentfault 主頁(yè)

原創(chuàng)禁止私自轉(zhuǎn)載

異步處理方案系列- 1.callback 引言

異步/異步操作,已經(jīng)是前端領(lǐng)域一個(gè)老生常談的話題.也是做前端開(kāi)發(fā)中經(jīng)常面臨的一個(gè)問(wèn)題.

然而異步的問(wèn)題往往比較復(fù)雜且難于處理, 特別是異步問(wèn)題還經(jīng)常不是多帶帶出現(xiàn),往往存在比較多樣的組合關(guān)系.

在實(shí)際處理中就顯得更加復(fù)雜而難于處理. 特別是在 io 操作頻繁,或者 node server 中,經(jīng)常遇到非常復(fù)雜的組合型異步。

舉個(gè)業(yè)務(wù)開(kāi)發(fā)中常見(jiàn)的例子:

eg: 省市縣三級(jí)級(jí)聯(lián)問(wèn)題

這個(gè)問(wèn)題非常常見(jiàn), 假設(shè)數(shù)據(jù)量較大, 我們大多數(shù)情況下不會(huì)一次加載所有的數(shù)據(jù), 然后做前端級(jí)聯(lián)的方案.

而是采取三個(gè)數(shù)據(jù)接口,在下拉改變的時(shí)候去動(dòng)態(tài)請(qǐng)求的方式.這就形成一種很常見(jiàn)的多個(gè)異步串行的模型.

怎么處理這樣的問(wèn)題, 怎么較好的維護(hù)多個(gè)異步之間的關(guān)系, 怎么讓代碼正常執(zhí)行的同時(shí),在邏輯和結(jié)構(gòu)上更可讀呢?

我將會(huì)梳理

callback

cps

thunk

defer / promise(非 es6)

promise(ES6)

generator -> co.

async / await

這幾種處理方式. 加上兩種模式

事件監(jiān)聽(tīng)

訂閱發(fā)布模型

列出一個(gè)系列的博客去討論這個(gè)問(wèn)題.
看我們?cè)诓煌A段, 使用不同技術(shù),如何處理相同的問(wèn)題. 在不同方案之間橫向?qū)Ρ? 去深入了解
技術(shù)變遷以及背后的處理思路和邏輯的變化.

callback

什么是回調(diào)呢? 這么問(wèn)似乎有點(diǎn)多余, 每個(gè)寫(xiě)過(guò) javascript 的開(kāi)發(fā)者, 或多或少都會(huì)接觸到回調(diào). 回調(diào)的使用成本很低,
實(shí)現(xiàn)回調(diào)函數(shù)就像傳遞一般的參數(shù)變量一樣簡(jiǎn)單.由于函數(shù)式編程極好的支持,以至于這項(xiàng)技術(shù)使用基本沒(méi)有障礙.我們隨手就能寫(xiě)出一個(gè)回調(diào)

Ajax.get("http://xxxx", {}, (resp) => {
    // .....
})

但是呢,要真給回調(diào)下一個(gè)定義, 也還真不好回答.

我們不妨從一些側(cè)面去看看回調(diào)

回調(diào)是一種處理特定問(wèn)題的模式, 伴隨著函數(shù)式編程而生. 函數(shù)式編程中很重要的技術(shù)之一就是回調(diào)函數(shù)

當(dāng)一個(gè)函數(shù)作為主調(diào)函數(shù)的參數(shù)時(shí), 它往往會(huì)在特定的時(shí)間和場(chǎng)景(上下文)中執(zhí)行.

執(zhí)行過(guò)程中,回調(diào)函數(shù)選擇性接收函數(shù)內(nèi)部的數(shù)據(jù), 或者狀態(tài)(內(nèi)存), 經(jīng)過(guò)處理選擇性返回,或者改變狀態(tài)(hock).

callback 業(yè)務(wù)模型

說(shuō)這么多, 我們不如從代碼的角度去解決一個(gè)串行的異步模型.

為了說(shuō)明問(wèn)題, 我們將問(wèn)題簡(jiǎn)化成 A B C 三個(gè)異步(可能是 io, 網(wǎng)絡(luò)請(qǐng)求, 或者其他.為了方面描述, 我們采用 settimeout 來(lái)模擬), 這三個(gè)異步耗時(shí)不確定, 但是必須按照 A B C 的順序處理他們的返回結(jié)果.

處理這個(gè)問(wèn)題, 我們基本上有兩種思路:

控制異步發(fā)出的順序, 在 a 返回之后再發(fā) b 請(qǐng)求, 這樣將問(wèn)題串行化(省市縣模型中經(jīng)常需要省的返回值去請(qǐng)求省所對(duì)應(yīng)的市).

同時(shí)發(fā)出異步請(qǐng)求,控制處理的順序.

方案一: 串行化請(qǐng)求
// 模擬 ajax 函數(shù)
function ajax(url) {
    return function (cb) {
        setTimeout(function() {
            cb({
                url
            });
        }, Math.random() * 3000);
    }
}

// 初始化出三個(gè)請(qǐng)求
const A = ajax("/ofo/a");
const B = ajax("/ofo/b");
const C = ajax("/ofo/c");

// 控制請(qǐng)求順序
log("ajax A send...");
A(function (a) {
    log("ajax A receive...");

    log("ajax B send...");
    B(function (b) {
        log("ajax B receive...");

        log("ajax C send...");
        C(function (C) {
            log("ajax C receive...");
        });
    })
})

代碼很簡(jiǎn)單, 大多是方案也是這么走的, 因?yàn)?A 的返回值可以作為 B 的參數(shù).
但是相應(yīng)的這個(gè)模式的總時(shí)間必定大于三個(gè)請(qǐng)求的時(shí)間之和.輸出如下:

ajax A send...
ajax A receive...
ajax B send...
ajax B receive...
ajax C send...
ajax C receive...
方案二: 自由請(qǐng)求,串行化處理
是相對(duì)不那么通用的方案, 但是處理沒(méi)有直接數(shù)據(jù)依賴的串行請(qǐng)求非常合適.
// 發(fā)送容器
const sender = [];
// 稍作改造
function ajax(url, time) {
    return function(cb) {
        // 記錄發(fā)送順序, 必須有序
        sender.push(url);
        setTimeout(function() {
            const data = {
                from: url,
                reso: "ok"
            };

            // 將 data, 回調(diào)傳遞給一個(gè)處理函數(shù)
            dealReceive({url, cb, data});
        }, time);
    }
}


// 按照順序處理返回結(jié)果

// 返回結(jié)果容器
const receiver = {};
function dealReceive({url, cb, data}) {
    // 記錄返回結(jié)果.可以無(wú)序
    receiver[url] = {cb, data};
    for (var i = 0; i < sender.length; i++) {
        let operate = receiver[sender[i]];
        if(typeof operate === "object") {

            operate.cb.call(null, operate.data);
        } else {
            return;
        }
    }
}

// 手動(dòng)模擬出請(qǐng)求時(shí)間, A 最耗時(shí).b 最快, 更好說(shuō)明問(wèn)題
const A = ajax("/ofo/a", 4000);
const B = ajax("/ofo/b", 600);
const C = ajax("/ofo/c", 2000);


// 注意我們的調(diào)用方式 是沒(méi)有任何控制的
// A,B,C 依次發(fā)出. 還可以按照這個(gè)順序處理 A,B,C 的返回值
A(function (a) {
    log(a);
});

B(function (b) {
    log(b);
});

C(function (c) {
    log(c);
});

輸出:

{"from":"/ofo/a","reso":"ok"}
{"from":"/ofo/b","reso":"ok"}
{"from":"/ofo/c","reso":"ok"}

這種方案總耗時(shí)基本上是耗時(shí)最長(zhǎng)的 ajax 的耗時(shí)。

值得注意的是, A,B,C 的調(diào)用上沒(méi)有做任何控制. A 最耗時(shí), 但是要最最先處理 A 的返回?cái)?shù)據(jù).
實(shí)現(xiàn)這一點(diǎn)的關(guān)鍵就在于我們 dealReceive 有個(gè)輪詢, 這個(gè)輪詢不是定時(shí)觸發(fā)的,而是每當(dāng)請(qǐng)求回來(lái)時(shí), 觸發(fā)輪詢. 整個(gè)過(guò)程輪詢 3 次.

基本上 callback 處理組合異步模型的思路說(shuō)完了.串行是容易處理的一種模型, 如果出現(xiàn) c 依賴 a,b 都正確返回的模型時(shí), 基本上我們暴力一點(diǎn)就是轉(zhuǎn)化為串行關(guān)系. 盡管 a, b 沒(méi)有關(guān)系.
或者呢我們就在 a, b 的回調(diào)里做標(biāo)志位. 和 dealReceive 類似.

單個(gè)異步不需要有太多處理, callback 的一些細(xì)節(jié)也不做討論. 主要討論是回調(diào)在實(shí)際場(chǎng)景中的處理問(wèn)題方案

回調(diào)兩面性

我們還是落入俗套的分析一下回調(diào)的優(yōu)缺點(diǎn).其實(shí)主要是缺點(diǎn).

優(yōu)點(diǎn): 使用成本低, 處理簡(jiǎn)單問(wèn)題非常方便.能夠拿到主調(diào)函數(shù)內(nèi)部的環(huán)境.等等.

大多數(shù)人認(rèn)為的缺點(diǎn):

回調(diào)很 low: 可能是因?yàn)? 實(shí)現(xiàn)回調(diào)函數(shù)就像傳遞一般的參數(shù)變量一樣簡(jiǎn)單.由于函數(shù)式編程極好的支持,以至于這項(xiàng)技術(shù)使用基本沒(méi)有障礙.也沒(méi)有比較嚴(yán)格的模式要求.大家習(xí)以為常了.

回調(diào)地獄(代碼橫向發(fā)展): 其實(shí)這并不是回調(diào)的錯(cuò). 當(dāng)我們遇到回調(diào)無(wú)底洞的時(shí)候,也無(wú)需驚慌,其實(shí)這根本不是什么問(wèn)題, 因?yàn)橥瑯佑袇f(xié)程和 monad 無(wú)底洞。因?yàn)槿绻惆讶魏我粋€(gè)抽象使用地足夠頻繁的話,都同樣會(huì)創(chuàng)造一個(gè)無(wú)底洞。

使用回調(diào)上的建議: 沒(méi)有使用障礙導(dǎo)致回調(diào)的濫用, 大部分問(wèn)題都用了簡(jiǎn)單的回調(diào)堆疊來(lái)解決. 實(shí)際上我們有很多基于回調(diào)的模式可以避免這些問(wèn)題.比如: cps, cps 進(jìn)一步轉(zhuǎn)化為 thunk.等等.

這樣看來(lái), 回調(diào)沒(méi)有缺點(diǎn), 是這樣么? 不是的. 回調(diào)有非常致命的機(jī)制上的缺點(diǎn), 這個(gè)問(wèn)題可能在 node 中爆發(fā),除非自身改變,或者被吃掉。

所謂的機(jī)制就是:你可能在用回調(diào)處理復(fù)雜問(wèn)題的時(shí)候,對(duì)自己能力產(chǎn)生懷疑,這些異步之間的關(guān)系是那么難以梳理清晰,而又難以寫(xiě)出容易維護(hù)的代碼.

其實(shí)這都不是你的錯(cuò).

使用回調(diào)處理異步往往意味著,你舍棄了返回值,而使用回調(diào)接收異步操作結(jié)果. 而這正是用回調(diào)風(fēng)格來(lái)編程會(huì)很困難的根本原因: 回調(diào)風(fēng)格不返回任何值,所以難以組合[函數(shù)式編程中函數(shù)有良好的輸入和輸出是函數(shù)可以組合的根本]。

一個(gè)沒(méi)有返回值的函數(shù)執(zhí)行的效果其實(shí)是利用它的副作用

一個(gè)沒(méi)有返回值和利用副作用的函數(shù)其實(shí)就是一個(gè)黑洞。

所以,使用回調(diào)風(fēng)格來(lái)編程無(wú)法避免會(huì)是指令式的,它實(shí)際上是通過(guò)把一系列嚴(yán)重依賴于副作用的操作安排好執(zhí)行順序,而不是通過(guò)函數(shù)的調(diào)用來(lái)把輸入輸出值對(duì)應(yīng)好。如果你是通過(guò)回調(diào)組織程序執(zhí)行流程, 而不是靠理順值的關(guān)系來(lái)解決問(wèn)題的, 是很難編寫(xiě)出正確的并行程序

這種問(wèn)題也間接的導(dǎo)致了回調(diào)難于調(diào)試,定位問(wèn)題和維護(hù).

最終的結(jié)果就是:  你崩潰了

注:系列博客陸續(xù)推出,稍安勿躁。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/100060.html

相關(guān)文章

  • 《Node.js設(shè)計(jì)模式》基于回調(diào)的異步控制流

    摘要:編寫(xiě)異步代碼可能是一種不同的體驗(yàn),尤其是對(duì)異步控制流而言。回調(diào)函數(shù)的準(zhǔn)則在編寫(xiě)異步代碼時(shí),要記住的第一個(gè)規(guī)則是在定義回調(diào)時(shí)不要濫用閉包。為回調(diào)創(chuàng)建命名函數(shù),避免使用閉包,并將中間結(jié)果作為參數(shù)傳遞。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書(shū)筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關(guān)注我的專欄,之后的博文將在專...

    Chiclaim 評(píng)論0 收藏0
  • 小程序生命周期——小程序探索

    摘要:運(yùn)行機(jī)制小程序啟動(dòng)會(huì)有兩種情況,一種是冷啟動(dòng),一種是熱啟動(dòng)。建議小程序在必要時(shí)使用監(jiān)聽(tīng)內(nèi)存告警事件,進(jìn)行必要的內(nèi)存清理。 前言 以小程序?yàn)榍腥朦c(diǎn),深入理解總結(jié)方方面面的知識(shí)點(diǎn),做成系列文章,希望能得到大神的指點(diǎn)和幫助新人入門(mén),承上啟下才是好程序猿由于是系列第一篇文章,緊跟著的是一大段廢話,只關(guān)心技術(shù)的可以跳過(guò) 轉(zhuǎn)眼半年又要過(guò)去了,意味著來(lái)新公司快半年了,離上次寫(xiě)文章也半年了,渾渾噩噩...

    Caizhenhao 評(píng)論0 收藏0
  • 談?wù)凧avaScript異步代碼優(yōu)化

    摘要:異步問(wèn)題回調(diào)地獄首先,我們來(lái)看下異步編程中最常見(jiàn)的一種問(wèn)題,便是回調(diào)地獄。同時(shí)使用也是異步編程最基礎(chǔ)和核心的一種解決思路。基于,目前也被廣泛運(yùn)用,其是異步編程的一種解決方案,比傳統(tǒng)的回調(diào)函數(shù)解決方案更合理和強(qiáng)大。 關(guān)于 微信公眾號(hào):前端呼啦圈(Love-FED) 我的博客:勞卜的博客 知乎專欄:前端呼啦圈 前言 在實(shí)際編碼中,我們經(jīng)常會(huì)遇到Javascript代碼異步執(zhí)行的場(chǎng)景...

    chnmagnus 評(píng)論0 收藏0
  • JS 異步(callback→Promise→async/await)

    摘要:異步編程三座大山原型原型鏈作用域閉包同步異步。異步操作執(zhí)行完畢后,再執(zhí)行該回調(diào)函數(shù),確?;卣{(diào)在異步操作之后執(zhí)行。回調(diào)函數(shù)本身是我們約定俗成的一種叫法,我們定義它,但是并不會(huì)自己去執(zhí)行它,它最終被其他人執(zhí)行了。 JS異步編程 JS三座大山:原型原型鏈、作用域閉包、同步異步。之前有寫(xiě)過(guò)自己對(duì)閉包的理解,今天來(lái)總結(jié)一下JS中的異步。 思考(案例來(lái)自stackoverflow): functi...

    gougoujiang 評(píng)論0 收藏0
  • Promise源碼學(xué)習(xí)(1

    摘要:工作當(dāng)中經(jīng)常會(huì)用到,在此進(jìn)行深入學(xué)習(xí)異步編程解決方案是異步編程的一種解決方案,比傳統(tǒng)的解決方案回調(diào)函數(shù)和事件更合理和更強(qiáng)大。所有源碼注釋見(jiàn)學(xué)習(xí)筆記 工作當(dāng)中經(jīng)常會(huì)用到Promise,在此進(jìn)行深入學(xué)習(xí) 異步編程解決方案 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn),ES6 將其寫(xiě)進(jìn)了語(yǔ)言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了...

    young.li 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<