【javascript】異步編年史,從“純回調”到Promise

異步和分塊——程序的分塊執行

?
一開始學習javascript的時候, 我對異步的概念一臉懵逼, 因為當時百度了很多文章,但很多各種文章不負責任的把籠統的描述混雜在一起,讓我對這個 JS中的重要概念難以理解, “異步是非阻塞的”, “Ajax執行是異步的”, "異步用來處理耗時操作"....?
?
所有人都再說這個是異步的,那個是異步的,異步會怎么怎樣,可我還是不知道:“異步到底是什么?”
?
后來我發現,其實理解異步最主要的一點,就是記住: 我們的程序是分塊執行的
?
分成兩塊, 同步執行的湊一塊, 異步執行的湊一塊,搞完同步,再搞異步
廢話不多說, 直接上圖:
?圖1

?

圖2?
?

?

異步和非阻塞

?

我對異步的另外一個難以理解的點是異步/同步和阻塞/非阻塞的關系
人們常說: “異步是非阻塞的” , 但為什么異步是非阻塞的, 或者說, 異步和非阻塞又有什么關系呢
?
非阻塞是對異步的要求, 異步是在“非阻塞”這一要求下的必然的解決方式?
?
咱們看看一個簡單的例子吧
ajax("http://XXX.", callback);
doOtherThing()

?

你肯定知道ajax這個函數的調用是發出請求取得一些數據回來, 這可能需要相當長的一段時間(相比于其他同步函數的調用)

?
對啊,如果我們所有代碼都是同步的,這就意味著, 在執行完ajax("http://XXX.", callback)這段代碼前, doOtherThing這個函數是不會執行的在外表看起來, 我們的程序不就“阻塞”在ajax("http://XXX.", callback);這個函數里了么? 這就是所謂的阻塞啊
?
讓我們再想一想doOtherThing因為“同步”造成“阻塞”的話會有多少麻煩: doOtherThing()里面包含了這些東西: 這個簡略的函數代表了它你接下來頁面的所有的交互程序, 但你現在在ajax執行結束前,你都沒有辦法去doOtherThing,去做接下來所有的交互程序了。 在外觀上看來, 頁面將會處于一個“完全假死”的狀態。
?
因為我們要保證在大量ajax(或類似的耗時操作)的情況下,交互能正常進行
所以同步是不行的
因為同步是不行的, 所以這一塊的處理, 不就都是異步的嘛
?
如果這樣還不太理解的話, 我們反方向思考一下, 假設一個有趣的烏托邦場景: 假設ajax的執行能像一個同步執行的foreach函數的執行那樣迅速, javascript又何苦對它做一些異步處理呢? 就是因為它如此耗時, 所以javascript“審時度勢”, 拿出了“異步”的這一把刷子,來解決問題
?
正因為有“非阻塞”的剛需, javascript才會對ajax等一概采用異步處理
?
“因為要非阻塞, 所以要異步”,這就是我個人對異步/同步和阻塞/非阻塞關系的理解
?
可能你沒有注意到,回調其實是存在很多問題的
?
沒錯,接下來的畫風是這樣子的:

?

回調存在的問題

?
回調存在的問題可概括為兩類:
?

信任問題和控制反轉

可能你比較少意識到的一點是:我們是無法在主程序中掌控對回調的控制權的。
例如:
?
ajax( "..", function(..){    } );

?

我們對ajax的調用發生于現在,這在 JavaScript 主程序的直接控制之下。但ajax里的回調會延遲到將來發生,并且是在第三方(而不是我們的主程序)的控制下——在本例中就是函數 ajax(..) 。這種控制權的轉移, 被叫做“控制反轉”
?
1.調用函數過早
?
調用函數過早的最值得讓人注意的問題, 是你不小心定義了一個函數,使得作為函數參數的回調可能延時調用,也可能立即調用。?? 也即你使用了一個可能同步調用, 也可能異步調用的回調。 這樣一種難以預測的回調。
?
大多數時候,我們的函數總是同步的,或者總是異步的
例如foreach()函數總是同步的
array.foreach(x =>? console.log(x)
)
console.log(array)

?

雖然foreach函數的調用需要一定的時間,但array數組的輸出一定是在所有的數組元素都被輸出之后才輸出, 因為foreach是同步的
又如setTimeout總是異步的:
setTimeout( () => {? console.log('我是異步的')? }, )
console.log('我是同步的')

?

有經驗的JS老司機們一眼就能看出, 一定是輸出
?
我是同步的
我是異步的

?

而不是
?
我是異步的
我是同步的

?

但有些時候,我們仍有可能會寫出一個既可能同步, 又可能異步的函數,
例如下面這個極簡的例子:
我試圖用這段代碼檢查一個輸入框內輸入的賬號是否為空, 如果不為空就用它發起請求。(注:callback無論賬號是否為空都會被調用)
?
// 注: 這是一個相當烏托邦,且省略諸多內容的函數
function login (callback) {// 當取得的賬號變量name的值為空時, 立即調用函數,此時callback同步調用)if(!name) {callback();return?? // name為空時在這里結束函數
??????? }// 當取得的賬號變量name的值不為空時, 在請求成功后調用函數(此時callback異步調用)request('post', name, callback)
}

?

相信各位機智的園友憑第六感就能知曉:這種函數絕B不是什么好東西。
?
的確,這種函數的編寫是公認的需要杜絕的,在英語世界里, 這種可能同步也可能異步調用的回調以及包裹它的函數, 被稱作是 “Zalgo” (一種都市傳說中的魔鬼), 而編寫這種函數的行為, 被稱作是"release Zalgo" (將Zalgo釋放了出來)
?
為什么它如此可怕? 因為函數的調用時間是不確定的,難以預料的。 我想沒有人會喜歡這樣難以掌控的代碼
例如:
var a =1
zalgoFunction () {// 這里還有很多其他代碼,使得a = 2可能被異步調用也可能被同步調用[??a = 2? ]}
console.log(a)

?

結果會輸出什么呢?? 如果zalgoFunction是同步的, 那么a 顯然等于2, 但如果 zalgoFunction是異步的,那么 a顯然等于1。于是, 我們陷入了無法判斷調用影響的窘境。
?
這只是一個極為簡單的場景, 如果場景變得相當復雜, 結果又會如何呢?
?
你可能想說: 我自己寫的函數我怎么會不知道呢?
請看下面:
1. 很多時候這個不確定的函數來源于它人之手,甚至來源于完全無法核實的第三方代碼
2. 在1的基礎上,我們把這種不確定的情況稍微變得夸張一些: 這個函數中傳入的回調, 有99%的幾率被異步調用, 有1%的幾率被同步調用
?
在1和2的基礎上, 你向一個第三方的函數傳了一個回調, 然后在經過了一系列不可描述的bug后......
?

?

2.調用次數過多
?
這里取《你不知道的javascript(中卷)》的例子給大家看一看:
?
作為一個公司的員工, 你需要開發一個網上商城, payWithYourMoney是你在確認購買后執行的扣費的函數, 由于公司需要對購買的數據做追蹤分析, 這里需要用到一個做數據分析的第三方公司提供的analytics對象中的purchase函數。 代碼看起來像這樣
?
analytics.purchase( purchaseData, function? () {payWithYourMoney ()
} );

?

在這情況下,可能我們會忽略的一個事實是: 我們已經把payWithYourMoney 的控制權完全交給了analytics.purchase函數了,這讓我們的回調“任人宰割”
?
然后上線后的一天, 數據分析公司的一個隱蔽的bug終于顯露出來, 讓其中一個原本只執行一次的payWithYourMoney執行了5次, 這讓那個網上商城的客戶極為惱怒, 并投訴了你們公司。
?
你們公司也很無奈, 這個時候驚奇的發現:?? payWithYourMoney的控制完全不在自己的手里 !!!!!
后來, 為了保證只支付一次, 代碼改成了這樣:
?
var analysisFlag? = true // 判斷是否已經分析(支付)過一次了
analytics.purchase( purchaseData, function(){if (!analysisFlag) {payWithYourMoney ()analysisFlag = false}
} );

?

但是, 這種方式雖然巧妙, 但卻仍不夠簡潔優雅(后文提到的Promise將改變這一點)
而且, 在回調函數的無數“痛點”中, 它只能規避掉一個, 如果你嘗試規避掉所有的“痛點”,代碼將比上面更加復雜而混亂。
?
3.太晚調用或根本沒有調用
因為你失去了對回調的控制權, 你的回調可能會出現預期之外的過晚調用或者不調用的情況(為了處理這個“痛點”你又將混入一些復雜的代碼邏輯)
?
4.吞掉報錯
回調內的報錯是可能被包裹回調的外部函數捕捉而不報錯,(為了處理這個“痛點”你又又又將混入一些復雜的代碼邏輯)
?
5.回調根本沒有被調用

沒辦法在復雜的異步場景中很好地表達代碼邏輯

?
哎呀這里我就不說廢話了:?在異步中如果你總是依賴回調的話,很容易就寫出大家都看不懂, 甚至自己過段時間也看不懂的代碼來, 嗯, 就這樣
?
看個例子,下面的doA到doF都是異步的函數
doA( function(){doB();doC( function(){doD();} )doE();
} );
doF();

?

請問這段代碼的調用順序 ? 當然你知道它肯定不是A -> B -> C -> D -> E,但即使你富有經驗,一般也得花上一段時間的功夫才能把它理清楚吧。( A → B → C → D → E → F 。)
?
這并不是我們開發人員的鍋, 而是因為人腦的思維方式本來就是線性的, 而回調卻打破了這種線性的思維, 我們需要強制地拋棄我們看到的A -> B -> C -> D -> E的順序,去構建另一套思維。
?
所以說,異步編程中有大量回調混雜的時候, 所造成的可讀性差的問題,是回調本身的“表達方式“造成的
?
?

?

?
回調的局限性僅僅如此? NO,請看下面:
?
對于一些比較常見的異步場景回調也沒辦法用足夠簡潔優雅的方式去處理:
這些場景包括但不限于:鏈式,門和競態
?
鏈式
首先你肯定知道用回調處理大量存在鏈式的異步場景的畫風是怎樣的
例如這樣:
setTimeout(function (name) {var catList = name + ','setTimeout(function (name) {catList += name + ',';setTimeout(function (name) {catList += name + ',';setTimeout(function (name) {catList += name + ',';setTimeout(function (name) {catList += name;console.log(catList);}, 1, 'Lion');}, 1, 'Snow Leopard');}, 1, 'Lynx');}, 1, 'Jaguar');}, 1, 'Panther');

?

讓人一臉蒙逼的回調函數地獄
?
很顯然,大多數時候你嘗試這樣做,是因為
你需要通過調用第一層異步函數,取得結果
然后把結果傳給第二層異步函數,第二層異步函數也取得結果后
傳遞結果給第三個異步函數, 。。。。。 N
?
很顯然,我們的代碼風格應該是“鏈式”風格, 但卻因為回調的原因被硬生生折騰成了難懂的“嵌套”風格! (別擔心, 我下面介紹的Promise將改變這一點)
?
什么叫“門”?, 你可以大概理解成: 現在有一群人準備進屋,但只有他們所有人都到齊了,才能“進門” ,也就是: 只有所有的異步操作都完成了, 我們才認為它整體完成了,才能進行下一步操作
?
下面這個例子里, 我們試圖通過兩個異步請求操作,分別取得a和b的值并將它們以 a + b的形式
(前提: 我們希望當a和b的取值都到達的時候才輸出!!)
var a, b;
function foo(x) {a = x * 2;if (a && b) {baz();}
}
function bar(y) {b = y * 2;if (a && b) {baz();}
}
function baz() {console.log( a + b );
}
// ajax(..)是某個庫中的某個Ajax函數
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

?

這段代碼比前面那段“鏈式”里的回調地獄好懂多了,但是卻依然存在這一些問題:
?
我們使用了兩個? if (a && b) { }? 去分別保證baz是在a和b都到達后才執行的,試著思考一下:
兩個? if (a && b) { }? 的判斷條件是否可以合并到一起呢,因為這兩個判斷條件都試圖表達同一種語意: a 和 b都到達, 能合并成一條語句的話豈不是更加簡潔優雅 ? (一切都在為Promise做鋪墊哦~~~~啦啦啦)
?
競態(可能跟你一般理解的競態有些不同)
一組異步操作,其中一個完成了, 這組異步操作便算是整體完成了
?
在下面,我們希望通過異步請求的方式,取得x的值,然后執行foo或者bar,但希望只把foo或者bar其中一個函數執行一次
?
var flag = true;
function foo(x) {if (flag) {x = x + 1baz(x);flag = false}
}
function bar(x) {if (flag) {x = x*2baz(x);flag = false}
}
function baz( x ) {console.log( x );
}
// ajax(..)是某個庫中的某個Ajax函數
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

?

在這里,我們設置了一個flag, 設它的初始值為true, 這時候foo或者bar在第一次執行的時候, 是可以進入if內部的代碼塊并且執行baz函數的, 但在if內部的代碼塊結束的時候, 我們把flag的值置為false,這個時候下一個函數就無法進入代碼塊執行了, 這就是回調對于競態的處理
?
正因為回調給我們帶來的麻煩很多,ES6引入了Promise的機制:
?
?

一步一步地揭開Promise神秘的面紗

?
首先讓我們回顧一下“回調函數”給我們帶來信任危機的原因: 我們無法信任放入回調參數的函數, 因為 它沒有強制要求通過一種確定的(或固定的)形式給我們回調傳遞有效的信息參數,例如: 異步操作成功的信息, 異步操作失敗的信息,等等。 我們既然都無從得到這些信息, 又怎么能擁有對回調的控制權呢?
?
沒錯,我們急需做的的就是得到這些對我們的“回調”至關重要的信息(異步操作成功的信息, 異步操作失敗的信息), 并且通過一種規則讓它們強制地傳遞給我們的回調
?
讓我們一步步來看看什么是Promise
1.首先Promise是一個可以包含異步操作的對象
new Promise(function() {/* 異步操作? */
}

?

2.其次, 這個對象擁有自己的狀態(state),可以分別用來表示異步操作的“成功”, “失敗”,“正在進行中”。
它們是:
Fulfilled: 成功
Rejected:拒絕
Pending: 進行中
?
3.那怎么控制這三個狀態的改變呢?
當new 一個Promise對象的時候, 我們能接收到兩個方法參數: resolve和reject, 當調用 resolve方法的時候,會把Promise對象的狀態從Pending變為Fulfilled(表示異步操作成功了),當調用 reject方法的時候, 會把Promise對象的狀態從Pending變為Rejected,表示異步操作失敗了, 而如果這兩個函數沒有調用,則Promise對象的狀態一直是Pending(表示異步操作正在進行)
?
我們異步執行的函數可以放在Promise對象里, 然后變成這樣
var promise = new Promise(function(resolve, reject) {// 這里是一堆異步操作的代碼if (/* 異步操作成功 */){resolve(value);} else {reject(error);}
});

?

4. 最重要的一點, 我們怎么把這個狀態信息傳遞給我們異步處理后的函數:
?
我們剛剛說了, Promise有Resolved和Rejected兩種狀態, 這兩種狀態分別對應Promise的then方法里的兩個回調參數
promise.then(function(value) {// 成功
}, function(error) {// 失敗
});

?

第一個參數方法對應Resolved, 第二個參數方法對應Rejected
而且Promise成功的時候(調用resolve), resolve返回的參數可以被第一個回調接收到, 如上面的value參數
而當Promise失敗的時候(調用reject), reject返回的錯誤會被傳遞給第二個回調, 如上面的error
?
【辯解】: 你可能會說:哎呀我們繞了一圈不是又回到了回調了嗎? Promise好像也不是特別革命性的一個新東西嘛!但是, 我們就圍繞信任問題來說, Promise的確以一種強制的方式, 將回調的形式固定了下來(兩個方法參數),并且傳遞了必要的數據(異步取得的值或拋出的錯誤)給我們的回調。
?
而這樣做,我們已經達到了我們的目的: 相對來說,我們使得回調變得“可控”了, 而不是像單純使用回調那樣, 因為控制反轉而陷入信任危機的噩夢。
打個比方, 讓司機們依據對自身的道德要求讓不闖紅燈,和通過扣分的機制和法律限制闖紅燈的現象, 無論是性質上還是效果上,這兩者之間都是截然不同的。
?

Promise是怎么一個個地解決回調帶來的問題的

?

?

?

1.回調過早調用
讓我們回到那個回調的痛點:我們有可能會寫出一個既可能同步執行, 又可能異步執行的“zalgo”函數。但Promise可以自動幫我們避免這個問題:
?
如果對一個 Promise 調用 then(..) 的時候,即使這個 Promise是立即resolve的函數(即Promise內部沒有ajax等異步操作,只有同步操作), 提供給then(..) 的回調也是會被異步調用的,這幫助我們省了不少心
?
2. 回調調用次數過多
Promise 的內部機制決定了調用單個Promise的then方法, 回調只會被執行一次,因為Promise的狀態變化是單向不可逆的,當這個Promise第一次調用resolve方法, 使得它的狀態從pending(正在進行)變成fullfilled(已成功)或者rejected(被拒絕)后, 它的狀態就再也不能變化了
?
所以你完全不必擔心Promise.then( function ) 中的function會被調用多次的情況
?
3. 回調中的報錯被吞掉
要說明一點的是Promise中的then方法中的error回調被調用的時機有兩種情況:
1. Promise中主動調用了reject? (有意識地使得Promise的狀態被拒絕), 這時error回調能夠接收到reject方法傳來的參數(reject(error))
2. 在定義的Promise中, 運行時候報錯(未預料到的錯誤), 也會使得Promise的狀態被拒絕,從而使得error回調能夠接收到捕捉到的錯誤
例如:
var p = new Promise( function(resolve,reject){foo.bar(); // foo未定義,所以會出錯!resolve( 42 ); // 永遠不會到達這里 :(
} );
p.then(function fulfilled(){// 永遠不會到達這里 :(
??? },function rejected(err){// err將會是一個TypeError異常對象來自foo.bar()這一行
???? }
);

?

4. 還有一種情況是回調根本就沒有被調用,這是可以用Promise的race方法解決(下文將介紹)
// 用于超時一個Promise的工具
function timeoutPromise(delay) {return new Promise( function(resolve,reject){setTimeout( function(){reject( "Timeout!" );}, delay );} );
}

// 設置foo()超時 Promise.race( [foo(), // 試著開始foo()timeoutPromise( 3000 ) // 給它3秒鐘 ] ) .then(function(){// foo(..)及時完成!},function(err){// 或者foo()被拒絕,或者只是沒能按時完成// 查看err來了解是哪種情況} );

?

?

Promise的完善的API設計使得它能夠簡潔優雅地處理相對復雜的場景

鏈式

?
我們上面說了, 純回調的一大痛點就是“金字塔回調地獄”, 這種“嵌套風格”的代碼丑陋難懂,但Promise就可以把這種“嵌套”風格的代碼改裝成我們喜聞樂見的“鏈式”風格
因為then函數是可以鏈式調用的, 你的代碼可以變成這樣
Promise.then(// 第一個異步操作
).then(// 第二個異步操作
).then(// 第三個異步操作
)

?

?
而且, 你每一個then里面的異步操作可以返回一個值,傳遞給下一個異步操作
getJSON('/post/1.json').then(function(post) {return getJSON(post.commentURL);
}).then(function(comments) {// some code
})

?

第二個then接收到的comments參數等于都一個then里面接收到的getJSON(post.commentURL);
?
例如我們上面提到的

可以使用 Promise.all方法:
Promise.all([promise1,promise2
])
.then(([data1, data2]) =>? getDataAndDoSomething (data1,data2)

?

?
all方法接收一個Promise數組,并且返回一個新的“大Promise”, 只有數組里的全部Promise的狀態都轉為Fulfilled(成功),這個“大Promise”的狀態才會轉為Fulfilled(成功), 這時候, then方法里的成功的回調接收的參數也是數組,分別和數組里的子Promise一一對應, 例如promise1對應data1,promise2對應data2
?
而如果任意一個數組里的子Promise失敗了, 這個“大Promise”的狀態會轉為Rejected, 并且將錯誤參數傳遞給then的第二個回調
?

競態

可以用Promise.race方法簡單地解決
romise.race方法同樣是將多個Promise實例,包裝成一個新的“大Promise”
例如
var p = Promise.race([p1, p2, p3]);

?

上面代碼中,只要p1、p2、p3之中有一個Promise率先改變狀態,p的狀態就跟著改變。那個率先改變的 Promise 實例的返回值,就傳遞給p的回調函數。
?
?
最后講個小故事
?
曾經我和小伙伴們搞比賽,合并代碼都是通過QQ傳代碼文件然后手動合并,經常會為代碼的管理不勝其煩, 遇到諸多問題。一個學長告訴我可以用git,但我當時卻覺得:“用QQ傳代碼合并就很好嘛, 用git的話學起來又麻煩,合并代碼辛苦一點也很正常的嘛~~~”,直到有一天我真的用上了git這個可愛的版本控制系統 ——
?
當初勸我用git的學長的溫暖的身影就浮現出來了....額...就像這樣:
?

?

如果不對新的東西加以學習, 你可能不知道舊的東西會給你帶來多少麻煩
如果永遠執著于舊的那一套東西, 你可能不知道新的東西能給你帶來多少希望和機遇
?
所以不要總是說:“用原來的就挺好的呀”
?
?
參考資料:《 你不知道的javascript》—— [美] Kyle Simpson

?

轉載于:https://www.cnblogs.com/penghuwan/p/7451409.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251957.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251957.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251957.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Shell編程之if語法練習(LNMP)全過程

大家好,我是延凱,本人原來在CSDN寫作已經快一年了 都是相關Linux運維這方面的技術知識,現在搬到博客園也是我一直想的,本博客主要寫Python,docker,shell等偏向開發云計算等知識點,謝謝各位&…

基于UNet和camvid數據集的道路分割

基于UNet和camvid數據集的道路分割h(1.3.0): 背景 語義分割是深度學習中的一個非常重要的研究方向,并且UNet是語義分割中一個非常經典的模型。在本次博客中,我嘗試用UNet對camvid dataset數據集進行道路分割,大致期望的效果如下&…

二分法查找和普通查找

一、普通查找 對于數組和一個需要查找的元素來說,普通查找的原理很簡單,即為從數組的第一個元素到最后一個元素進行遍歷,如果第i個元素的值等于我們需要查找的值,那么返回找到的角標i,否則返回-1表示沒有查找到。這里以…

Linux下安裝zookeeper集群(奇數個)

1、 解壓zookeeper壓縮包 2、 data里創建“myid”文件(命令touch myid),內容是1(命令 echo 1 >> myid) 3、 zoo.cnf里配置dataDir、clientport、server.nIP:端口1(2881):端…

立體標定

立體標定應用標定數據轉換成深度圖標定 由于攝像頭目前是我們手動進行定位的,我們現在還不知道兩張圖像與世界坐標之間的耦合關系,所以下一步要進行的是標定,用來確定分別獲取兩個攝像頭的內部參數,并且根據兩個攝像頭在同一個世…

if _name_ == _main_

1.作用 py文件有2種使用方法,第1是自己本腳本自己獨立執行;第2是被import到其他文件腳本中執行. if _name_ " _main_" 該語句控制其他下一步的腳本是否執行。如果是自己本腳本獨立執行,那就運行該if條件下的腳本;如果…

LLVM完整參考安裝

文章目錄 一、直接下載編譯好的,見圖片命令二、下載源代碼自己編譯安裝 下面提供下載并mv完全的文件包三、安裝LLVM編譯器一、直接下載編譯好的,見圖片命令 這里使用llvm官網編譯好的包, 直接解壓即可用LLVM下載官網點擊這里下載llvm-6.0.1 下載完成后解壓tar -vxf clangllv…

微軟正式釋出基于 Chromium 的 Edge 預覽版本

百度智能云域名服務,.com新用戶首購僅需25元 微軟基于 Chromium 的全新版本 Edge 一直吸引著開發者與用戶的目光,當地時間 8 日,官方終于釋出了第一個 Dev 和 Canary 頻道構建版本。 Dev 與 Canary build 都是開發者預覽版,同屬…

下載和安裝R、RStudio !

現如今,R語言是統計領域廣泛使用的工具,是屬于GNU系統的一個自由、免費、源代碼開放的軟件,是用于統計計算和統計繪圖的優秀工具。而RStudio是R的集成開發環境,用它進行R編程的學習和實踐會更加輕松和方便。下面就教大家如何下載并…

豆瓣首頁話題輸入框的實現

在做問答的時候,遇到一個需求,用戶的問題需要限制字數,不僅顯示計算的超出字數,還需在超出的內容上加一些提醒的效果,例如豆瓣首頁的話題輸入框,抽時間研究了下,需要考慮下面幾個問題&#xff1…

pytorch 吸煙檢測yolov5s

YOLOV5s 吸煙目標檢測 參考學習 文章目錄 本原創項目長期更新,旨在完成校園異常行為實時精檢測,作到集成N次開發優化(不止局限于調包)為止,近期將不斷更新如下模型數據標注文件教程。關注博主,Star 一下g…

JQuery的ajax函數執行失敗,alert函數彈框一閃而過

先查看<form>標簽是否有action屬性&#xff0c;如果沒有&#xff0c;并且最后<button>標簽的type屬性為submit‘時&#xff0c;默認提交位置就是當前頁面 如果在頁面右鍵檢查&#xff0c;點擊網絡&#xff0c;會在開頭發現這樣的post包&#xff1a; 在右側消息頭處…

C#中Request.ServerVariables詳細說明及代理

Request.ServerVariables("Url") 返回服務器地址Request.ServerVariables("Path_Info") 客戶端提供的路徑信息Request.ServerVariables("Appl_Physical_Path") 與應用程序元數據庫路徑相應的物理路徑Request.ServerVariables("Path_Transla…

coco與voc相互轉化

把LabelImg標注的YOLO格式標簽轉化為VOC格式標簽 和 把VOC格式標簽轉化為YOLO格式標簽 點亮&#xff5e;黑夜 2020-07-07 11:08:24 3537 已收藏 90 分類專欄&#xff1a; 19—目標檢測 文章標簽&#xff1a; voc yolo 版權 把LabelImg標注的YOLO格式標簽轉化為VOC格式標簽 和…

angular中封裝fancyBox(圖片預覽)

首先在官網下載最新版的fancyBox(一定要去最新網站&#xff0c;以前依賴的jquery版本偏低)&#xff0c;附上鏈接&#xff1a;http://fancyapps.com/fancybox/3/ 然后在項目中引用jquery&#xff0c;然后在引用jquery.fancybox.min.css和jquery.fancybox.min.js。 如果需要動畫和…

十二省聯考題解 - JLOI2019 題解

十二省聯考題解 - JLOI2019 題解 兩個T3的難度較大 平均代碼量遠大于去年省選 套路題考查居多 A 難度等級 1 $n^2$暴力可以拿到$60$分的優秀成績 然后可以想到把區間異或轉化為前綴兩點異或 可以想到使用二分答案的方法可持久化Trie解決&#xff0c;但是時間復雜度為$n\log^2 (…

前端vue的get和post請求

vue的get和post需要兩個文件vue.js和vue-resource.js 以下是實現的代碼&#xff0c;可以參考一下&#xff0c;需要注意的接口的請求需要考慮跨域的問題&#xff0c;其次就是訪問頁面需要在tomcat下訪問&#xff0c;否則也會報跨域的問題 <!DOCTYPE html> <html lang&q…

[Vijos 1143]三取方格數

Description 設有N*N的方格圖&#xff0c;我們將其中的某些方格填入正整數&#xff0c; 而其他的方格中放入0。 某人從圖得左上角出發&#xff0c;可以向下走&#xff0c;也可以向右走&#xff0c;直到到達右下角。 在走過的路上&#xff0c;他取走了方格中的數。&#xff08;取…

線掃相機相關規格說明

工業線陣相機與面陣相機特點分析 點滴成海~ 2018-06-29 13:50:38 12184 收藏 29 分類專欄&#xff1a; intership 文章標簽&#xff1a; 視覺元件分析 版權 最近在公司實習&#xff0c;實習中的項目是使用的是微視的一款線陣相機&#xff08;Microview MVC1024DLM-GE35&…

postgresql 不同數據庫不同模式下的數據遷移

編寫不容易,轉載請注明出處謝謝, 數據遷移 因為之前爬蟲的時候&#xff0c;一部分數據并沒有上傳到服務器&#xff0c;在本地。本來用的就是postgresql&#xff0c;也沒用多久&#xff0c;數據遷移的時候&#xff0c;也遇到了很多問題&#xff0c;第一次使pg_dump xx > file…