国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JS魔法堂:深究JS異步編程模型

idealcn / 3418人閱讀

摘要:而同步和異步則是描述另一個(gè)方面。異步將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間的操作由系統(tǒng)自動(dòng)處理,然后通知應(yīng)用程序直接使用數(shù)據(jù)即可。

前言

?上周5在公司作了關(guān)于JS異步編程模型的技術(shù)分享,可能是內(nèi)容太干的緣故吧,最后從大家的表情看出“這條粉腸到底在說(shuō)啥?”的結(jié)果:(下面是PPT的講義,具體的PPT和示例代碼在https://github.com/fsjohnhuan...上,有興趣就上去看看吧!

重申主題

?《異步編程模型》這個(gè)名稱確實(shí)不太直觀,其實(shí)今天我想和大家分享的就是上面的代碼是如何演進(jìn)成下面的代碼而已。

a(function(){
    b(function(){
        c(function(){
            d()
        })
    })
})

TO

;(async function(){
    await a()
    await b()
    await c()
    await d()
}())
寫(xiě)在前面

?我們知道JavaScript是單線程運(yùn)行的(撇開(kāi)Web Worker),并且JavaScript線程執(zhí)行時(shí)瀏覽器GUI渲染線程無(wú)法搶占CPU時(shí)間片,因此假如我們通過(guò)以下代碼實(shí)現(xiàn)60秒后執(zhí)行某項(xiàng)操作

const deadline = Date.now() + 60000
while(deadline > Date.now());
console.log("doSomething")

那么瀏覽器將假死60秒。正常情況下我們采用異步調(diào)用的方式來(lái)實(shí)現(xiàn)

const deadline = Date.now() + 60000
;(function _(){
    if (deadline > Date.now()){
        setTimeout(_, 100)
    }
    else{
        console.log("doSomething")
    }
}())

那到底上述兩種方式有什么不同呢?

到這里我有個(gè)疑問(wèn),那就是到底什么才叫做異步呢?既然有異步,那必然有同步,那同步又是什么呢?談起同步和異步,那必不可少地要提起阻塞和非阻塞,那它們又是什么意思呢?

談到它們那必須聯(lián)系到IO來(lái)說(shuō)了
阻塞: 就是JS線程發(fā)起阻塞IO后,JS線程什么都不做就等則阻塞IO響應(yīng)。
非阻塞: 就是JS線程發(fā)起非阻塞IO后,JS線程可以做其他事,然后通過(guò)輪詢、信號(hào)量等方式通知JS線程獲取IO響應(yīng)結(jié)果。
也就是說(shuō)阻塞和非阻塞描述的是發(fā)起IO和獲取IO響應(yīng)之間的時(shí)間里,JS線程是否可以繼續(xù)處理其他任務(wù)。
而同步和異步則是描述另一個(gè)方面。

首先當(dāng)我們發(fā)起網(wǎng)絡(luò)IO請(qǐng)求時(shí),應(yīng)用程序會(huì)向OS發(fā)起系統(tǒng)調(diào)用,然后內(nèi)核會(huì)調(diào)用驅(qū)動(dòng)程序操作網(wǎng)卡,然后網(wǎng)卡得到的數(shù)據(jù)會(huì)先存放在內(nèi)核空間中(應(yīng)用程序是讀取不了的),然后將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間。抽象一下就是,發(fā)起IO請(qǐng)求會(huì)涉及到用戶空間和內(nèi)核空間間的數(shù)據(jù)通信。

同步: 應(yīng)用程序需要顯式地將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間中,然后再使用數(shù)據(jù)。
異步: 將數(shù)據(jù)從內(nèi)核空間拷貝到用戶空間的操作由系統(tǒng)自動(dòng)處理,然后通知應(yīng)用程序直接使用數(shù)據(jù)即可。

對(duì)于如setTimeout等方法而已,本來(lái)就存在用戶空間和內(nèi)核空間的數(shù)據(jù)通信問(wèn)題,因此異步更多是描述非阻塞這一特性。
那么異步調(diào)用的特點(diǎn)就是:
1. 非阻塞
2. 操作結(jié)果將于不明確的未來(lái)返回

從Callback Hell說(shuō)起

舉個(gè)栗子——番茄炒蛋
番茄切塊(代號(hào)a)
雞蛋打成蛋液(代號(hào)b)
蛋液煮成半熟(代號(hào)c)
將蛋切成塊(代號(hào)d)
番茄與雞蛋塊一起炒熟(代號(hào)e)

假設(shè)個(gè)步驟都是同步IO時(shí)

->番茄切塊->雞蛋打成蛋液->蛋液煮成半熟->將蛋切成塊->番茄與雞蛋塊一起炒熟

a()
b()
c()
d()
e()
假設(shè)個(gè)步驟都是異步IO時(shí)

?情況1——所有步驟均無(wú)狀態(tài)依賴
->番茄切塊
->雞蛋打成蛋液
->蛋液煮成半熟
->將蛋切成塊
->番茄與雞蛋塊一起炒熟

a()
b()
c()
d()
e()

?情況2——步驟間存在線性的狀態(tài)依賴
->番茄切塊->雞蛋打成蛋液->蛋液煮成半熟->將蛋切成塊->番茄與雞蛋塊一起炒熟

a("番茄", function(v番茄塊){
    b("雞蛋", function(v蛋液){
        c(v蛋液, function(v半熟的雞蛋){
            d(v半熟的雞蛋, function(v雞蛋塊){
                e(v番茄塊, v雞蛋塊)
            })
        })
    })
})

這就是Callback Hell了

?情況3——步驟間存在復(fù)雜的狀態(tài)依賴
異步執(zhí)行:->番茄切塊 |->番茄與雞蛋塊一起炒熟

                ->雞蛋打成蛋液->蛋液煮成半熟->切成蛋塊|

異步調(diào)用所帶來(lái)的問(wèn)題是

狀態(tài)依賴關(guān)系難以表達(dá),更無(wú)法使用if...else,while等流程控制語(yǔ)句。

無(wú)法提供try...catch異常機(jī)制來(lái)處理異常

初次嘗試——EventProxy

EventProxy作為一個(gè)事件系統(tǒng),通過(guò)after、tail等事件訂閱方法提供帶約束的事件觸發(fā)機(jī)制,“約束”對(duì)應(yīng)“前置條件”,因此我們可以利用這種帶約束的事件觸發(fā)機(jī)制來(lái)作為異步執(zhí)行模式下的流程控制表達(dá)方式。

const doAsyncIO = (value, cb) => setTimeout(()=>cb(value), Math.random() * 1000)
const ep = new EventProxy()

/* 定義任務(wù) */
const a = v番茄 => doAsyncIO("番茄塊", ep.emit.bind(ep,"a"))
const b = v雞蛋 => doAsyncIO("蛋液", ep.emit.bind(ep,"b"))
const c = v蛋液 => doAsyncIO("半熟的雞蛋", ep.emit.bind(ep,"c"))
const d = v半熟的雞蛋 => doAsyncIO("雞蛋塊", ep.emit.bind(ep,"d"))
const e = (v番茄塊, v雞蛋塊) => doAsyncIO("番茄炒雞蛋", ep.emit.bind(ep,"e"))

/* 定義任務(wù)間的狀態(tài)依賴 */
ep.once("b",c)
ep.once("c",d)
ep.all("a", "d", e)

/* 執(zhí)行任務(wù) */
a()
b()

另外通過(guò)error事件提供對(duì)異常機(jī)制的支持

ep.on("error", err => {
    console.log(err)
})

但由于EventProxy采用事件機(jī)制來(lái)做流程控制,而事件機(jī)制好處是降低模塊的耦合度,但從另一個(gè)角度來(lái)說(shuō)會(huì)使整個(gè)系統(tǒng)結(jié)構(gòu)松散難以看出主干模塊,因此通過(guò)事件機(jī)制實(shí)現(xiàn)流程控制必然導(dǎo)致代碼結(jié)構(gòu)松散和邏輯離散,不過(guò)這可以良好的組織形式來(lái)讓代碼結(jié)構(gòu)更緊密一些。

曙光的出現(xiàn)——Promise

這里的Promise指的是已經(jīng)被ES6納入囊中的Promises/A+規(guī)范及其實(shí)現(xiàn).
Promise相當(dāng)于我們?nèi)湲?dāng)勞點(diǎn)餐后得到的小票,在未來(lái)某個(gè)時(shí)間點(diǎn)拿著小票就可以拿到食物。不同的是,只要我們持有Promise實(shí)例,無(wú)論索取多少次,都能拿到同樣的結(jié)果。而麥當(dāng)勞顯然只能給你一份食物而已。
代碼表現(xiàn)如下

const p1 = new Promise(function(resolve, reject){
    /*    工廠函數(shù)
     *    resolve函數(shù)表示當(dāng)前Promise正常結(jié)束, 例子: setTimeout(()=>resolve("bingo"), 1000)
     *    reject函數(shù)表示當(dāng)前Promise發(fā)生異常, 例子: setTimeout(()=>reject(Error("OMG!")), 1000)
     */
})
const p2 = p1.then(
    function fulfilled(val){
        return val + 1
    }
    , function rejected(err){
        /*處理p1工廠函數(shù)中調(diào)用reject傳遞來(lái)的值*/
    }
)
const p3 = p2.then(
    function fulfilled(val){
        return new Promise(function(resolve){setTimeout(()=>resolve(val+1), 10000)})
    }
    , function rejected(err){
        /*處理p1或p2調(diào)用reject或throw error的值*/
    }
)
p3.catch(function rejected(err){
        /*處理p1或p2或p3調(diào)用reject或throw error的值*/
    }
)

Promises/A+中規(guī)定Promise狀態(tài)為pending(默認(rèn)值)、fulfilled或rejected,其中狀態(tài)僅能從pending->fulfilled或pending->rejected,并且可通過(guò)then和catch訂閱狀態(tài)變化事件。狀態(tài)變化事件的回調(diào)函數(shù)執(zhí)行結(jié)果會(huì)影響Promise鏈中下一個(gè)Promise實(shí)例的狀態(tài)。另外在觸發(fā)Promise狀態(tài)變化時(shí)是可以攜帶附加信息的,并且該附加信息將沿著Promise鏈被一直傳遞下去直到被某個(gè)Promise的事件回調(diào)函數(shù)接收為止。而且Promise還提供Promise.all和Promise.race兩個(gè)幫助方法來(lái)實(shí)現(xiàn)與或的邏輯關(guān)系,提供Promsie.resolve來(lái)將thenable對(duì)象轉(zhuǎn)換為Promise對(duì)象。
API:
new Promise(function(resolve, reject){}), 帶工廠函數(shù)的構(gòu)造函數(shù)
Promise.prototype.then(fulfilled()=>{}, rejected()=>{}),訂閱Promise實(shí)例狀態(tài)從pending到fulfilled,和從pending到rejected的變化
Promise.prototype.catch(rejected()=>{}),訂閱Promise實(shí)例狀態(tài)從pending到rejected的變化
Promise.resolve(val), 生成一個(gè)狀態(tài)為fulfilled的Promise實(shí)例
Promise.reject(val), 生成一個(gè)狀態(tài)為rejected的Promise實(shí)例
Promise.all(array), 生成一個(gè)Promise實(shí)例,當(dāng)array中所有Promise實(shí)例狀態(tài)均為fulfilled時(shí),該P(yáng)romise實(shí)例的狀態(tài)將從pending轉(zhuǎn)換為fulfilled,若array中某個(gè)Promise實(shí)例的狀態(tài)為rejected,則該實(shí)例的狀態(tài)將從pending轉(zhuǎn)換為rejected.
Promise.race(array), 生成一個(gè)Promise實(shí)例,當(dāng)array中某個(gè)Promise實(shí)例狀態(tài)發(fā)生轉(zhuǎn)換,那么該P(yáng)romise實(shí)例也隨之轉(zhuǎn)

const doAsyncIO = value => resolve => setTimeout(()=>resolve(value), Math.random() * 1000)

/* 定義任務(wù) */
const a = v番茄 => new Promise(doAsyncIO("番茄塊"))
const b = v雞蛋 => new Promise(doAsyncIO("蛋液"))
const c = v蛋液 => new Promise(doAsyncIO("半熟的雞蛋"))
const d = v半熟的雞蛋 => new Promise(doAsyncIO("雞蛋塊"))
const e = ([v番茄塊, v雞蛋塊]) => new Promise(doAsyncIO("番茄炒雞蛋"))

/* 執(zhí)行任務(wù) */
Promise.all([
    a("番茄"),
    b("雞蛋").then(c).then(d)
]).then(e)
    .catch(err=>{
        console.log(err)
    })

最大特點(diǎn):獨(dú)立的可存儲(chǔ)的異步調(diào)用結(jié)果
其他特點(diǎn):fulfilled和rejected函數(shù)異步執(zhí)行

jQuery作為前端必備工具,也為我們提供類似與Promise的工具,那就是jQuery.Deffered

const deffered = $.getJSON("dummy.js")
deffered.then(function(val1){
    console.log(val1)
    return !val1
},function (err){
    console.log(err)
}).then(function(val2){
    console.log(val2)
})

但jQuery.Deferred并不是完整的Promise/A+的實(shí)現(xiàn)。
如:

jQuery1.8之前上述代碼val2的值與val1一樣,jQuery1.8及以后上述代碼val2的值就是!val1了。

fulfilled和rejected函數(shù)采用同步執(zhí)行

遺留問(wèn)題!

const a = () => Promise.resolve("a")
const b = (v1) => Promise.resolve("b")
const c = (v2, v1) => console.log(v1)

a().then(b).then(c)
真正的光明——Coroutine

?Coroutine中文就是協(xié)程,意思就是線程間采用協(xié)同合作的方式工作,而不是搶占式的方式工作。由于JS是單線程運(yùn)行的,所以這里的Coroutine就是一個(gè)可以部分執(zhí)行后退出,后續(xù)可在之前退出的地方繼續(xù)往下執(zhí)行的函數(shù).

function coroutine(){
    yield console.log("c u later!")
    console.log("welcome guys!")
}
Generator Function

?其實(shí)就是迭代器,跟C#的IEnumrable、IEnumerator和Java的Iterable、Iterator一樣。

function* enumerable(){
    yield 1
    yield 2
}
for (let num of enumerable()){
    console.log(num)
}

?現(xiàn)在我們將1,2替換為代碼

function *enumerable(msg){
  console.log(msg)
  var msg1 = yield msg + " after " // 斷點(diǎn)
  console.log(msg1)
  var msg2 = yield msg1 + " after" // 斷點(diǎn)
  console.log(msg2 + " over")
}

編譯器會(huì)將上述代碼轉(zhuǎn)換成

const enumerable = function(msg){
  var state = -1

  return {
    next: function(val){
      switch(++state){
         case 0:
                  console.log(msg + " after")
                  break
         case 1:
                  var msg1 = val
                  console.log(msg1 + " after")
                  break
         case 2:
                  var msg2 = val
                  console.log(msg2 + " over")
                  break
      }
    }
  }
}

通過(guò)調(diào)用next函數(shù)就可以從之前退出的地方繼續(xù)執(zhí)行了。(條件控制、循環(huán)、迭代、異常捕獲處理等就更復(fù)雜了)
其實(shí)Generator Function實(shí)質(zhì)上就是定義一個(gè)有限狀態(tài)機(jī),然后通過(guò)Generator Function實(shí)例的next,throw和return方法觸發(fā)狀態(tài)遷移。
next(val), 返回{value: val1, done: true|false}
throw(err),在上次執(zhí)行的位置拋出異常
return(val),狀態(tài)機(jī)的狀態(tài)遷移至終止態(tài),并返回{value: val, done: true}
現(xiàn)在我們用Gererator Function來(lái)做番茄炒蛋

const doAsyncIO = value => (resolve) => setTimeout(()=>resolve(value), Math.random() * 1000)

/* 定義任務(wù) */
const a = v番茄 => new Promise(doAsyncIO("番茄塊"))
const b = v雞蛋 => new Promise(doAsyncIO("蛋液"))
const c = v蛋液 => new Promise(doAsyncIO("半熟的雞蛋"))
const d = v半熟的雞蛋 => new Promise(doAsyncIO("雞蛋塊"))
const e = (v番茄塊, v雞蛋塊) => new Promise(doAsyncIO("番茄炒雞蛋"))

function* coroutineFunction(){
    try{
        var p番茄塊 = a("番茄")
        var v蛋液 = yield b("雞蛋")
        var v半熟的雞蛋 = yield c(v蛋液)
        var v雞蛋塊 = yield d(v半熟的雞蛋)
        var v番茄塊 = yield p番茄塊
        var v番茄抄雞蛋 = yield e(v番茄塊, v雞蛋塊)
    }
    catch(e){
        console.log(e.message)
    }
}
const coroutine = coroutineFunction()
throwError = coroutine.throw.bind(coroutine)
coroutine.next().value.then(function(v蛋液){
    coroutine.next(v蛋液).then(function(v半熟的雞蛋){
        coroutine.next(v半熟的雞蛋).then(function(v雞蛋塊){
            coroutine.next().then(function(v番茄塊){
                coroutine.next(v番茄塊).then(function(v番茄抄雞蛋){
                    coroutine.next(v番茄抄雞蛋)
                }, throwError)
            }, throwError)
        }, throwError)
    }, throwError)
})

?悲催又回到Callback hell.但我們可以發(fā)現(xiàn)coroutineFunction其實(shí)是以同步代碼的風(fēng)格來(lái)定義任務(wù)間的執(zhí)行順序(狀態(tài)依賴)而已,執(zhí)行模塊在后面這個(gè)讓人頭痛的Callback hell那里,并且這個(gè)Callback Hell是根據(jù)coroutineFunction的內(nèi)容生成,像這種重復(fù)有意義的事情自然由機(jī)器幫我們處理最為恰當(dāng)了,于是我們引入個(gè)狀態(tài)管理器得到

const doAsyncIO = value => (resolve) => setTimeout(()=>resolve(value), Math.random() * 1000)

/* 定義任務(wù) */
const a = v番茄 => new Promise(doAsyncIO("番茄塊"))
const b = v雞蛋 => new Promise(doAsyncIO("蛋液"))
const c = v蛋液 => new Promise(doAsyncIO("半熟的雞蛋"))
const d = v半熟的雞蛋 => new Promise(doAsyncIO("雞蛋塊"))
const e = (v番茄塊, v雞蛋塊) => new Promise(doAsyncIO("番茄炒雞蛋"))

function* coroutineFunction(){
    try{
        var p番茄塊 = a("番茄")
        var v蛋液 = yield b("雞蛋")
        var v半熟的雞蛋 = yield c(v蛋液)
        var v雞蛋塊 = yield d(v半熟的雞蛋)
        var v番茄塊 = yield p番茄塊
        var v番茄抄雞蛋 = yield e(v番茄塊, v雞蛋塊)
    }
    catch(e){
        console.log(e.message)
    }
}
iPromise(coroutineFunction)

?舒爽多了!

async和await

ES7引入了async和await兩個(gè)關(guān)鍵字,Node.js7支持這兩貨。于是Coroutine寫(xiě)法就更酸爽了.

const doAsyncIO = value => (resolve) => setTimeout(()=>resolve(value), Math.random() * 1000)

/* 定義任務(wù) */
const a = v番茄 => new Promise(doAsyncIO("番茄塊"))
const b = v雞蛋 => new Promise(doAsyncIO("蛋液"))
const c = v蛋液 => new Promise(doAsyncIO("半熟的雞蛋"))
const d = v半熟的雞蛋 => new Promise(doAsyncIO("雞蛋塊"))
const e = (v番茄塊, v雞蛋塊) => new Promise(doAsyncIO("番茄炒雞蛋"))

async function coroutine(){
    try{
        var p番茄塊 = a("番茄")
        var v蛋液 = await b("雞蛋")
        var v半熟的雞蛋 = await c(v蛋液)
        var v雞蛋塊 = await d(v半熟的雞蛋)
        var v番茄塊 = await p番茄塊
        var v番茄抄雞蛋 = await e(v番茄塊, v雞蛋塊)
    }
    catch(e){
        console.log(e.message)
    }
}
coroutine()
總結(jié)

到這里各位應(yīng)該會(huì)想“不就做個(gè)西紅柿炒雞蛋嗎,搞這么多,至于嗎?”。其實(shí)我的看法是

對(duì)于狀態(tài)依賴簡(jiǎn)單的情況下,callback的方式足矣;

對(duì)于狀態(tài)依賴復(fù)雜(譬如做個(gè)佛跳墻等大菜時(shí)),Promise或Coroutine顯然會(huì)讓代碼更簡(jiǎn)潔直觀,更容易測(cè)試因此bug更少,更容易維護(hù)因此更易被優(yōu)化。

我曾夢(mèng)想有一天所有瀏覽器都支持Promise,async和await,大家可以不明就里地寫(xiě)出coroutine,完美地處理異步調(diào)用的各種問(wèn)題。直到有一天知道世上又多了Rxjs這貨,不說(shuō)了繼續(xù)填坑去:)

尊重原創(chuàng),轉(zhuǎn)載請(qǐng)注明來(lái)自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/81073.html

相關(guān)文章

  • WebComponent魔法:深究Custom Element 之 面向痛點(diǎn)編程

    摘要:前言最近加入到新項(xiàng)目組負(fù)責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以為代表的技術(shù)線,于是查閱各類資料想說(shuō)服老大向這方面靠,最后得到的結(jié)果是資料是英語(yǔ)無(wú)所謂,最重要是上符合要求,技術(shù)的事你說(shuō)了算。但當(dāng)我們需要?jiǎng)討B(tài)實(shí)例化元素時(shí),命令式則是最佳的選擇。 前言 ?最近加入到新項(xiàng)目組負(fù)責(zé)前端技術(shù)預(yù)研和選型,一直偏向于以Polymer為代表的WebComponent技術(shù)線,于是查閱各類資料想說(shuō)服老大向這方面...

    flyer_dev 評(píng)論0 收藏0
  • WebComponent魔法:深究Custom Element 之 標(biāo)準(zhǔn)構(gòu)建

    摘要:明確各階段適合的操作用于初始化元素的狀態(tài)和設(shè)置事件監(jiān)聽(tīng),或者創(chuàng)建。事件類型轉(zhuǎn)換通過(guò)捕獲事件,然后通過(guò)發(fā)起事件來(lái)對(duì)事件類型進(jìn)行轉(zhuǎn)換,從而觸發(fā)更符合元素特征的事件類型。 前言 ?通過(guò)《WebComponent魔法堂:深究Custom Element 之 面向痛點(diǎn)編程》,我們明白到其實(shí)Custom Element并不是什么新東西,我們甚至可以在IE5.5上定義自己的alert元素。但這種簡(jiǎn)單...

    philadelphia 評(píng)論0 收藏0
  • CSS魔法:display:none與visibility:hidden的恩怨情仇

    摘要:不耽誤表單提交數(shù)據(jù)雖然我們無(wú)法看到的元素,但當(dāng)表單提交時(shí)依然會(huì)將隱藏的元素的值提交上去。讓元素在見(jiàn)面上不可視,但保留元素原來(lái)占有的位置。不過(guò)由于各瀏覽器實(shí)現(xiàn)效果均有出入,因此一般不會(huì)使用這個(gè)值。繼承父元素的值。 前言 ?還記得面試時(shí)被問(wèn)起請(qǐng)說(shuō)說(shuō)display:none和visibility:hidden的區(qū)別嗎?是不是回答完display:none不占用原來(lái)的位置,而visibilit...

    selfimpr 評(píng)論0 收藏0
  • 前端魔法——異常不僅僅是try/catch

    摘要:我打算分成前端魔法堂異常不僅僅是和前端魔法堂調(diào)用棧,異常實(shí)例中的寶藏兩篇分別敘述內(nèi)置自定義異常類,捕獲運(yùn)行時(shí)異常語(yǔ)法異常網(wǎng)絡(luò)請(qǐng)求異常事件,什么是調(diào)用棧和如何獲取調(diào)用棧的相關(guān)信息。 前言 ?編程時(shí)我們往往拿到的是業(yè)務(wù)流程正確的業(yè)務(wù)說(shuō)明文檔或規(guī)范,但實(shí)際開(kāi)發(fā)中卻布滿荊棘和例外情況,而這些例外中包含業(yè)務(wù)用例的例外,也包含技術(shù)上的例外。對(duì)于業(yè)務(wù)用例的例外我們別無(wú)它法,必須要求實(shí)施人員與用戶共同...

    bladefury 評(píng)論0 收藏0
  • JS魔法之實(shí)戰(zhàn):純前端的圖片預(yù)覽

    摘要:一前言圖片上傳是一個(gè)普通不過(guò)的功能,而圖片預(yù)覽就是就是上傳功能中必不可少的子功能了。偶然從上找到純前端圖片預(yù)覽的相關(guān)資料,經(jīng)過(guò)整理后記錄下來(lái)以便日后查閱。類型為,表示在讀取文件時(shí)發(fā)生的錯(cuò)誤,只讀。 一、前言   圖片上傳是一個(gè)普通不過(guò)的功能,而圖片預(yù)覽就是就是上傳功能中必不可少的子功能了。在這之前,我曾經(jīng)通過(guò)訂閱input[type=file]元素的onchange事件,一旦更改路徑...

    岳光 評(píng)論0 收藏0

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

0條評(píng)論

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