摘要:從源碼看概念與實(shí)現(xiàn)是異步編程中的重要概念,它較好地解決了異步任務(wù)中回調(diào)嵌套的問(wèn)題。這些概念中有趣的地方在于,標(biāo)識(shí)狀態(tài)的變量如都是形容詞,用于傳入數(shù)據(jù)的接口如與都是動(dòng)詞,而用于傳入回調(diào)函數(shù)的接口如及則在語(yǔ)義上用于修飾動(dòng)詞的副詞。
從源碼看 Promise 概念與實(shí)現(xiàn)
Promise 是 JS 異步編程中的重要概念,它較好地解決了異步任務(wù)中回調(diào)嵌套的問(wèn)題。在沒(méi)有引入新的語(yǔ)言機(jī)制的前提下,這是如何實(shí)現(xiàn)的呢?上手 Promise 時(shí)常見(jiàn)若干晦澀的 API 與概念,它們又為什么存在呢?源碼里隱藏著這些問(wèn)題的答案。
下文會(huì)在介紹 Promise 概念的基礎(chǔ)上,以一步步代碼實(shí)現(xiàn) Promise 的方式,解析 Promise 的實(shí)現(xiàn)機(jī)制。相應(yīng)代碼參考來(lái)自 PromiseJS 博客 及 You don’t know JS 的若干章節(jié)。
Why Promise
(有使用 Promise 經(jīng)驗(yàn)的讀者可忽略本段)
基于 JS 函數(shù)一等公民的優(yōu)良特性,JS 中最基礎(chǔ)的異步邏輯一般是以向異步 API 傳入一個(gè)函數(shù)的方式實(shí)現(xiàn)的,這個(gè)函數(shù)里包含了異步完成后的后續(xù)業(yè)務(wù)邏輯。與普通的函數(shù)參數(shù)不同的是,這類(lèi)函數(shù)需在異步操作完成時(shí)才被調(diào)用,故而稱之為回調(diào)函數(shù)。以異步 Ajax 查詢?yōu)槔诨卣{(diào)的代碼實(shí)現(xiàn)可能是這樣的:
ajax.get("xxx", data => { // 在回調(diào)函數(shù)里獲取到數(shù)據(jù),執(zhí)行后續(xù)邏輯 console.log(data) // ... })
從而,在需要多個(gè)異步操作依次執(zhí)行時(shí),就需要以回調(diào)嵌套的方式來(lái)實(shí)現(xiàn),例如這樣:
ajax.get("xxx", dataA => { // 第一個(gè)請(qǐng)求完成后,依賴其獲取到的數(shù)據(jù)發(fā)送第二個(gè)請(qǐng)求 // 產(chǎn)生回調(diào)嵌套 ajax.get("yyy" + dataA, dataB => { console.log(dataB) // ... }) })
這樣一來(lái),在處理越多的異步邏輯時(shí),就需要越深的回調(diào)嵌套,這種編碼模式的問(wèn)題主要有以下幾個(gè):
代碼邏輯書(shū)寫(xiě)順序與執(zhí)行順序不一致,不利于閱讀與維護(hù)。
異步操作的順序變更時(shí),需要大規(guī)模的代碼重構(gòu)。
回調(diào)函數(shù)基本都是匿名函數(shù),bug 追蹤困難。
回調(diào)函數(shù)是被第三方庫(kù)代碼(如上例中的 ajax )而非自己的業(yè)務(wù)代碼所調(diào)用的,造成了 IoC 控制反轉(zhuǎn)。
其中看似最無(wú)關(guān)緊要的控制反轉(zhuǎn),實(shí)際上是純回調(diào)編碼模式的最大問(wèn)題。 由于回調(diào)函數(shù)是被第三方庫(kù)調(diào)用的,因此回調(diào)中的代碼無(wú)法預(yù)期自己被執(zhí)行時(shí)的環(huán)境 ,這可能導(dǎo)致:
回調(diào)被執(zhí)行了多次
回調(diào)一次都沒(méi)有被執(zhí)行
回調(diào)不是異步執(zhí)行而是被同步執(zhí)行
回調(diào)被過(guò)早或過(guò)晚執(zhí)行
回調(diào)中的報(bào)錯(cuò)被第三方庫(kù)吞掉
……
通過(guò)【防御性編程】的概念,上述問(wèn)題其實(shí)都可以通過(guò)在回調(diào)函數(shù)內(nèi)部進(jìn)行各種檢查來(lái)逐一避免,但這毫無(wú)疑問(wèn)地會(huì)嚴(yán)重影響代碼的可讀性與開(kāi)發(fā)效率。這種異步編碼模式存在的諸多問(wèn)題,也就是臭名昭著的【回調(diào)地獄】了。
Promise 較好地解決了這個(gè)問(wèn)題。以上例中的異步 ajax 邏輯為例,基于 Promise 的模式是這樣的:
// 將 ajax 請(qǐng)求封裝為一個(gè)返回 Promise 的函數(shù) function getData (){ return new Promise((resolve, reject) => { ajax.get("xxx", data => { resolve(data) }) }) } // 調(diào)用該函數(shù)并在 Promise 的 then 接口中獲取數(shù)據(jù) getData().then(data => { console.log(data) })
看起來(lái)變得啰嗦了?但在上例中需要嵌套回調(diào)的情況,可以改寫(xiě)成下面的形式:
function getDataA (){ return new Promise((resolve, reject) => { ajax.get("xxx", dataA => { resolve(dataA) }) }) } function getDataB (dataA){ return new Promise((resolve, reject) => { ajax.get("yyy" + dataA, dataB => { resolve(dataB) }) }) } // 使用鏈?zhǔn)秸{(diào)用解開(kāi)回調(diào)嵌套 getDataA() .then(dataA => getDataB(dataA)) .then(dataB => console.log(dataB))
這就解決了異步邏輯的回調(diào)嵌套問(wèn)題。那么問(wèn)題來(lái)了,這樣優(yōu)雅的 API 是如何實(shí)現(xiàn)的呢?
基礎(chǔ)概念
非?;\統(tǒng)地說(shuō),Promise 其實(shí)應(yīng)驗(yàn)了 CS 的名言【所有問(wèn)題都可以通過(guò)加一層中間層來(lái)解決】。在上面回調(diào)嵌套的問(wèn)題中,Promise 就充當(dāng)了一個(gè)中間層,用來(lái)【把回調(diào)造成的控制反轉(zhuǎn)再反轉(zhuǎn)回去】。在使用 Promise 的例子中,控制流分為了兩個(gè)部分:觸發(fā)異步前的邏輯通過(guò) new傳入 Promise,而異步操作完成后的邏輯則傳入 Promise 的 then 接口中。通過(guò)這種方式,第一方業(yè)務(wù)和第三方庫(kù)的相應(yīng)邏輯都由 Promise 來(lái)調(diào)用,進(jìn)而在 Promise 中解決異步編程中可能出現(xiàn)的各種問(wèn)題。
這種模式其實(shí)和觀察者模式是接近的。下面的代碼將 resolve / then 換成了 publish / subscribe ,將通過(guò) new Promise 生成的 Promise 換成了通過(guò) observe 生成的 observable 實(shí)例??梢园l(fā)現(xiàn),這種調(diào)用同樣做到了回調(diào)嵌套的解耦。這就是 Promise 魔法的關(guān)鍵之一。
// observe 相當(dāng)于 new Promise // publish 相當(dāng)于 resolve let observable = observe(publish => { ajax.get("xxx", data => { // ... publish(data) }) }) // subscribe 相當(dāng)于 then observable.subscribe(data => { console.log(data) // ... })
到這個(gè)例子為止,都還沒(méi)有涉及 Promise 的源碼實(shí)現(xiàn)。在進(jìn)一步深入前,有必要列出在 Promise 中常見(jiàn)的相關(guān)概念:
resolve / reject : 作為 Promise 暴露給第三方庫(kù)的 API 接口,在異步操作完成時(shí)由第三方庫(kù)調(diào)用,從而改變 Promise 的狀態(tài)。
fulfilled / rejected / pending : 標(biāo)識(shí)了一個(gè) Promise 當(dāng)前的狀態(tài)。
then / done : 作為 Promise 暴露給第一方代碼的接口,在此傳入【原本直接傳給第三方庫(kù)】的回調(diào)函數(shù)。
這些概念中有趣的地方在于,標(biāo)識(shí)狀態(tài)的變量(如 fulfilled / rejected / pending )都是形容詞,用于傳入數(shù)據(jù)的接口(如 resolve 與 reject )都是動(dòng)詞,而用于傳入回調(diào)函數(shù)的接口(如 then 及 done )則在語(yǔ)義上用于修飾動(dòng)詞的副詞。在閱讀源碼的時(shí)候,除了變量的類(lèi)型外,其名稱所對(duì)應(yīng)的詞性也能對(duì)理解代碼邏輯起到幫助,例如:
標(biāo)識(shí)數(shù)據(jù)的變量與 OO 對(duì)象常用名詞( result / data / Promise )
標(biāo)識(shí)狀態(tài)的變量常用形容詞( fulfilled / pending )
被調(diào)用的函數(shù)接口常用動(dòng)詞( resolve / reject )
用于傳入函數(shù)的參數(shù)接口常用副詞(如 then / onFulfilled 等,畢竟函數(shù)常用動(dòng)詞,而副詞本來(lái)就是用來(lái)修飾動(dòng)詞的)
預(yù)熱了 Promise 相關(guān)的變量名后,就可以開(kāi)始實(shí)現(xiàn) Promise 了。下文的行文方式既不是按行號(hào)逐行介紹,也不是按代碼執(zhí)行順序來(lái)回跳躍,而是按照實(shí)際編碼時(shí)的步驟一步步地搭建出相應(yīng)的功能。相信這種方式比直接在源碼里堆注釋能更為友好一些。
狀態(tài)機(jī)
一個(gè) Promise 可以理解為一個(gè)狀態(tài)機(jī),相應(yīng)的 API 接口要么用于改變狀態(tài)機(jī)的狀態(tài),要么在到達(dá)某個(gè)狀態(tài)時(shí)被觸發(fā)。因此首先需要實(shí)現(xiàn)的,是 Promise 的狀態(tài)信息:
const PENDING = 0 const FULFILLED = 1 const REJECTED = 2 function Promise (){ // 存儲(chǔ)該 Promise 的狀態(tài)信息 let state = PENDING // 存儲(chǔ) FULFILLED 或 REJECTED 時(shí)帶來(lái)的數(shù)據(jù) let value = null // 存儲(chǔ) then 或 done 時(shí)調(diào)用的成功或失敗回調(diào) var handlers = [] }
狀態(tài)遷移
指定狀態(tài)機(jī)的狀態(tài)后,可以實(shí)現(xiàn)基本的狀態(tài)遷移功能,即 fulfill 與 reject 這兩個(gè)用于改變狀態(tài)的函數(shù),相應(yīng)實(shí)現(xiàn)也十分簡(jiǎn)單:
const PENDING = 0 const FULFILLED = 1 const REJECTED = 2 function Promise (){ // 存儲(chǔ)該 Promise 的狀態(tài)信息 let state = PENDING // 存儲(chǔ) FULFILLED 或 REJECTED 時(shí)帶來(lái)的數(shù)據(jù) let value = null // 存儲(chǔ) then 或 done 時(shí)調(diào)用的成功或失敗回調(diào) let handlers = [] function fulfill (result){ state = FULFILLED value = result } function reject (error){ state = REJECTED value = error } }
在這兩種底層的狀態(tài)遷移基礎(chǔ)上,我們需要實(shí)現(xiàn)一種更高級(jí)的狀態(tài)遷移方式,這就是 resolve了:
const PENDING = 0 const FULFILLED = 1 const REJECTED = 2 function Promise (){ // 存儲(chǔ)該 Promise 的狀態(tài)信息 let state = PENDING // 存儲(chǔ) FULFILLED 或 REJECTED 時(shí)帶來(lái)的數(shù)據(jù) let value = null // 存儲(chǔ) then 或 done 時(shí)調(diào)用的成功或失敗回調(diào) let handlers = [] function fulfill (result){ state = FULFILLED value = result } function reject (error){ state = REJECTED value = error } function resolve (result){ try { let then = getThen(result) if (then) { // 遞歸 resolve 待解析的 Promise doResolve(then.bind(result), resolve, reject) return } fulfill(result) } catch (e) { reject(e) } } }
resolve 既可以接受一個(gè) Promise,也可以接受一個(gè)基本類(lèi)型。當(dāng) resolve 一個(gè) Promise 時(shí),就使用 doResolve 輔助函數(shù)來(lái)執(zhí)行這個(gè) Promise 并等待其完成。通過(guò)暴露 resolve 而隱藏底層的 fulfill 接口,從而保證了一個(gè) Promise 一定不會(huì)被另一個(gè) Promise 所 fulfill 。在這個(gè)過(guò)程中所用到的輔助函數(shù)如下:
/** * 檢查一個(gè)值是否為 Promise * 若為 Promise 則返回該 Promise 的 then 方法 * * @param {Promise|Any} value * @return {Function|Null} */ function getThen (value){ let t = typeof value if (value && (t === "object" || t === "function")) { const then = value.then // 可能需要更復(fù)雜的 thenable 判斷 if (typeof then === "function") return then } return null } /** * 傳入一個(gè)需被 resolve 的函數(shù),該函數(shù)可能存在不確定行為 * 確保 onFulfilled 與 onRejected 只會(huì)被調(diào)用一次 * 在此不保證該函數(shù)一定會(huì)被異步執(zhí)行 * * @param {Function} fn 不能信任的回調(diào)函數(shù) * @param {Function} onFulfilled * @param {Function} onRejected */ function doResolve (fn, onFulfilled, onRejected){ let done = false try { fn(function (value){ if (done) return done = true // 執(zhí)行由 resolve 傳入的 resolve 回調(diào) onFulfilled(value) }, function (reason){ if (done) return done = true onRejected(reason) }) } catch (ex) { if (done) return done = true onRejected(ex) } }
resolve 接口
在完整完成了內(nèi)部狀態(tài)機(jī)的基礎(chǔ)上,還需要向用戶暴露用于傳入第一方代碼的 new Promise接口,及傳入異步操作回調(diào)的 done / then 接口。下面從 resolve 一個(gè) Promise 開(kāi)始:
const PENDING = 0 const FULFILLED = 1 const REJECTED = 2 function Promise (fn){ // 存儲(chǔ)該 Promise 的狀態(tài)信息 let state = PENDING // 存儲(chǔ) FULFILLED 或 REJECTED 時(shí)帶來(lái)的數(shù)據(jù) let value = null // 存儲(chǔ) then 或 done 時(shí)調(diào)用的成功或失敗回調(diào) let handlers = [] function fulfill (result){ state = FULFILLED value = result } function reject (error){ state = REJECTED value = error } function resolve (result){ try { let then = getThen(result) if (then) { // 遞歸 resolve 待解析的 Promise doResolve(then.bind(result), resolve, reject) return } fulfill(result) } catch (e) { reject(e) } } doResolve(fn, resolve, reject) }
可以發(fā)現(xiàn)這里重用了 doResolve 以執(zhí)行不被信任的 fn 函數(shù)。這個(gè) fn 函數(shù)可以多次調(diào)用 resolve 和 reject 接口,甚至拋出異常,但 Promise 中對(duì)其進(jìn)行了限制,保證每個(gè) Promise 只能被 resolve 一次,且在 resolve 后不再發(fā)生狀態(tài)轉(zhuǎn)移。
觀察者 done 接口
到此為止已經(jīng)完成了一個(gè)完整的狀態(tài)機(jī),但仍然沒(méi)有暴露出一個(gè)合適的方法來(lái)觀察其狀態(tài)的變更。我們的最終目標(biāo)是實(shí)現(xiàn) then 接口,但由于實(shí)現(xiàn) done 接口的語(yǔ)義要容易得多,因此可首先實(shí)現(xiàn) done 。
下面的例子中要實(shí)現(xiàn)的是 promise.done(onFulfilled, onRejected) 接口,使得:
onFulfilled 與 onRejected 二者只有一個(gè)被調(diào)用。
該接口只會(huì)被調(diào)用一次。
該接口總是被異步執(zhí)行。
調(diào)用 done 的執(zhí)行時(shí)機(jī)與調(diào)用時(shí) Promise 是否已 resolved 無(wú)關(guān)。
const PENDING = 0 const FULFILLED = 1 const REJECTED = 2 function Promise (fn){ // 存儲(chǔ)該 Promise 的狀態(tài)信息 let state = PENDING // 存儲(chǔ) FULFILLED 或 REJECTED 時(shí)帶來(lái)的數(shù)據(jù) let value = null // 存儲(chǔ) then 或 done 時(shí)調(diào)用的成功或失敗回調(diào) let handlers = [] function fulfill (result){ state = FULFILLED handlers.forEach(handle) handlers = null } function reject (error){ state = REJECTED value = error handlers.forEach(handle) handlers = null } function resolve (result){ try { let then = getThen(result) if (then) { // 遞歸 resolve 待解析的 Promise doResolve(then.bind(result), resolve, reject) return } fulfill(result) } catch (e) { reject(e) } } // 保證 done 中回調(diào)的執(zhí)行 function handle (handler){ if (state === PENDING) { handlers.push(handler) } else { if (state === FULFILLED && typeof handler.onFulfilled === "function") { handler.onFulfilled(value) } if (state === REJECTED && typeof handler.onRejected === "function") { handler.onRejected(value) } } } this.done = function (onFulfilled, onRejected){ // 保證 done 總是異步執(zhí)行 setTimeout(function (){ handle({ onFulfilled: onFulfilled, onRejected: onRejected }) }, 0) } doResolve(fn, resolve, reject) }
從而在 Promise 的狀態(tài)遷移至 resolved 或 rejected 時(shí),所有通過(guò) done 注冊(cè)的觀察者 handler 都能被執(zhí)行。并且這個(gè)操作總是在下一個(gè) tick 異步執(zhí)行的。
觀察者 then 方法
在實(shí)現(xiàn)了 done 方法的基礎(chǔ)上,就可以實(shí)現(xiàn) then 方法了。它們沒(méi)有本質(zhì)的區(qū)別,但 then 能夠返回一個(gè)新的 Promise:
this.then = function (onFulfilled, onRejected){ const _this = this return new Promise(function (resolve, reject){ return _this.done(function (result){ if (typeof onFulfilled === "function") { try { return resolve(onFulfilled(result)) } catch (ex) { return reject(ex) } } else return resolve(result) }, function (error){ if (typeof onRejected === "function") { try { return resolve(onRejected(error)) } catch (ex) { return reject(ex) } } else return reject(error) }) }) }
最后梳理一下典型場(chǎng)景下 Promise 的執(zhí)行流程。以一個(gè) ajax 請(qǐng)求的異步場(chǎng)景為例,整個(gè)異步邏輯分為兩部分:調(diào)用 ajax 庫(kù)的代碼及異步操作完成時(shí)的代碼。前者被放入 Promise 的構(gòu)造函數(shù)中,由 doResolve 方法執(zhí)行,在這部分業(yè)務(wù)邏輯通過(guò)調(diào)用 resolve 與 reject 接口,在異步操作完成時(shí)改變 Promise 的狀態(tài),從而調(diào)用后者,即調(diào)用 Promise 中通過(guò) then 接口傳入的 onFulfilled 與 onRejected 后續(xù)業(yè)務(wù)邏輯代碼。這個(gè)過(guò)程中, doResolve 對(duì)第三方 ajax 庫(kù)的各種異常行為(多次調(diào)用回調(diào)或拋出異常)做了限制,而 then 下隱藏的 done 則封裝了 handle 接口,保證了多個(gè)通過(guò) then 傳入的 handler 總是異步執(zhí)行,并能得到合適的返回結(jié)果。由于then 中的代碼總是異步執(zhí)行并返回了一個(gè)新的 Promise,因此可以通過(guò)鏈?zhǔn)秸{(diào)用的方式來(lái)串聯(lián)多個(gè) then 方法,從而實(shí)現(xiàn)異步操作的鏈?zhǔn)秸{(diào)用。
總結(jié)
閱讀了 Promise 的代碼實(shí)現(xiàn)后可以發(fā)現(xiàn),它的魔法來(lái)自于將【函數(shù)一等公民】和【遞歸】的結(jié)合。一個(gè) resolve 如果獲得的結(jié)果還是一個(gè) Promise,那么就將遞歸地繼續(xù) resolve 這個(gè) Promise。同時(shí),Promise 的輔助函數(shù)中解決了諸多異步編程時(shí)的常見(jiàn)問(wèn)題,如回調(diào)的多次調(diào)用及異常處理等。
介紹 Promise 時(shí)不少較為晦澀的 API 其實(shí)也來(lái)自于對(duì) Promise 編碼實(shí)現(xiàn)時(shí)的涉及的若干底層功能。例如, fulfilled 這個(gè)概念就被封裝在了 resolve 下,而 done 方法則是 then 方法的依賴等。這些概念在 Promise 的演化中被封裝在了通用的 API 下,只有在閱讀源碼時(shí)才會(huì)用到。Promise 的 API 設(shè)計(jì)也是簡(jiǎn)潔的,其接口命名和英語(yǔ)的詞性也有相當(dāng)大的聯(lián)系,這也有利于理解代碼實(shí)現(xiàn)的相應(yīng)功能。
除了上文中從狀態(tài)機(jī)的角度理解 Promise 以外,其實(shí)還可以從函數(shù)式編程的角度來(lái)理解這個(gè)模式。可以將 Promise 看做一個(gè)封裝了異步數(shù)據(jù)的 Monad,其 then 接口就相當(dāng)于這個(gè) Monad 的map 方法。這樣一來(lái),Promise 也可以理解為一個(gè)特殊的對(duì)象,這個(gè)對(duì)象【通過(guò)一個(gè)函數(shù)獲取數(shù)據(jù),并通過(guò)另一個(gè)函數(shù)來(lái)操作數(shù)據(jù)】,用戶并不需要關(guān)心其中潛在的異步風(fēng)險(xiǎn),只需要提供相應(yīng)的函數(shù)給 Promise API 即可(這展開(kāi)又是一篇長(zhǎng)文了)。
希望本文對(duì) Promise 的分析對(duì)理解異步編程有所幫助
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/107940.html
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。寫(xiě)一個(gè)符合規(guī)范并可配合使用的寫(xiě)一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來(lái)處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問(wèn)題描述 在開(kāi)發(fā)過(guò)程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過(guò)http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過(guò)...
摘要:從最開(kāi)始的到封裝后的都在試圖解決異步編程過(guò)程中的問(wèn)題。為了讓編程更美好,我們就需要引入來(lái)降低異步編程的復(fù)雜性。異步編程入門(mén)的全稱是前端經(jīng)典面試題從輸入到頁(yè)面加載發(fā)生了什么這是一篇開(kāi)發(fā)的科普類(lèi)文章,涉及到優(yōu)化等多個(gè)方面。 TypeScript 入門(mén)教程 從 JavaScript 程序員的角度總結(jié)思考,循序漸進(jìn)的理解 TypeScript。 網(wǎng)絡(luò)基礎(chǔ)知識(shí)之 HTTP 協(xié)議 詳細(xì)介紹 HTT...
摘要:如果看完本文后,還對(duì)進(jìn)程線程傻傻分不清,不清楚瀏覽器多進(jìn)程瀏覽器內(nèi)核多線程單線程運(yùn)行機(jī)制的區(qū)別。因此準(zhǔn)備梳理這塊知識(shí)點(diǎn),結(jié)合已有的認(rèn)知,基于網(wǎng)上的大量參考資料,從瀏覽器多進(jìn)程到單線程,將引擎的運(yùn)行機(jī)制系統(tǒng)的梳理一遍。 前言 見(jiàn)解有限,如有描述不當(dāng)之處,請(qǐng)幫忙及時(shí)指出,如有錯(cuò)誤,會(huì)及時(shí)修正。 ----------超長(zhǎng)文+多圖預(yù)警,需要花費(fèi)不少時(shí)間。---------- 如果看完本文后,還...
摘要:我想這很好的解釋了中,僅僅一個(gè)都這么復(fù)雜,在單線程或者說(shuō)串行的程序中,編程往往是很簡(jiǎn)單的,說(shuō)白了就是調(diào)用,調(diào)用,調(diào)用然后返回。 Netty源碼分析(三) 前提概要 這次停更很久了,原因是中途迷茫了一段時(shí)間,不過(guò)最近調(diào)整過(guò)來(lái)了。不過(guò)有點(diǎn)要說(shuō)下,前幾天和業(yè)內(nèi)某個(gè)大佬聊天,收獲很多,所以這篇博文和之前也會(huì)不太一樣,我們會(huì)先從如果是我自己去實(shí)現(xiàn)這個(gè)功能需要怎么做開(kāi)始,然后去看netty源碼,與...
摘要:感謝大神的免費(fèi)的計(jì)算機(jī)編程類(lèi)中文書(shū)籍收錄并推薦地址,以后在倉(cāng)庫(kù)里更新地址,聲音版全文狼叔如何正確的學(xué)習(xí)簡(jiǎn)介現(xiàn)在,越來(lái)越多的科技公司和開(kāi)發(fā)者開(kāi)始使用開(kāi)發(fā)各種應(yīng)用。 說(shuō)明 2017-12-14 我發(fā)了一篇文章《沒(méi)用過(guò)Node.js,就別瞎逼逼》是因?yàn)橛腥嗽谥跎虾贜ode.js。那篇文章的反響還是相當(dāng)不錯(cuò)的,甚至連著名的hax賀老都很認(rèn)同,下班時(shí)讀那篇文章,竟然坐車(chē)的還坐過(guò)站了。大家可以很...
閱讀 2710·2023-04-25 20:19
閱讀 1968·2021-11-24 09:38
閱讀 1658·2021-11-16 11:44
閱讀 4482·2021-09-02 15:40
閱讀 1386·2019-08-30 15:55
閱讀 2046·2019-08-30 15:52
閱讀 3797·2019-08-29 17:20
閱讀 2330·2019-08-29 13:48