koa2長輪詢--一次promise遞歸的個(gè)人實(shí)踐

前面是廢話戒良,后面寫了點(diǎn)代碼,有興趣看的可以點(diǎn)擊直達(dá)
(點(diǎn)擊后會跳轉(zhuǎn)到新 tab 頁冠摄,位置是對的糯崎,編輯器不支持當(dāng)前 tab 跳轉(zhuǎn)不好意思啦。)

近期一直在用 iisnode+koa2河泳,想起之前有個(gè)小項(xiàng)目(類似聊天室)后端用的是 php 實(shí)現(xiàn)的長輪詢沃呢。本人由于是個(gè) php 渣渣,項(xiàng)目完成的非常粗糙拆挥,就動(dòng)了重構(gòu)的念頭薄霜。

不得不承認(rèn)的是,開始的本意是想 nodejs 嘗試 websock 改造纸兔,但是先后試了 ws和 socket.io 后惰瓜,我驚(jue)喜(wang)地發(fā)現(xiàn),項(xiàng)目沒有問題汉矿,本地測試也非常順利崎坊,確實(shí)是很優(yōu)雅很美好的通信方式(改天有空記錄一下 koa 框架折騰 websocket 的經(jīng)歷)。然而部署到線上之后洲拇,一切都沒法正常運(yùn)行了奈揍,原因是無法建立 websocket 連接。

納尼赋续?

嗯打月,我們線上服務(wù)器是 window server 2008

iisnode 的作者寫的很清楚,兩篇文章無論是使用 faye-websocket 還是 socket.io 蚕捉,首段都寫了需要 iis8 以上,即系統(tǒng)應(yīng)該是 window 8 或者 window server 2012 以上才可以使用柴淘。原因有兩個(gè)

  1. websocket 的支持需要 .Net 4.5 版本以上 (這個(gè)安裝倒是可以想辦法解決)
  2. 系統(tǒng)的核心組件 HTTP.SYS 需要在這兩個(gè)系統(tǒng)以上才支持 websocket

另外迫淹,社區(qū)還是有一些在低版本系統(tǒng)搭建 websocket 服務(wù)器的第三方庫的秘通,只是去解決這些問題怕是會碰到更棘手的狀況。如果有朋友有搭建經(jīng)驗(yàn)的敛熬,希望能分享一下~

事已至此肺稀,只好退而求其次,嘗試使用長輪詢方案來解決吧应民。
結(jié)合之前 php 實(shí)現(xiàn)的經(jīng)驗(yàn)梳理了一下代碼邏輯话原,整體思路應(yīng)該比較清晰

  1. 客戶端發(fā)起請求,使用原生的 XMLHttpRequest 或者 ajax 都可以诲锹,這里我用的是原生的XHR(不想為了ajax引入 jQuery或 zepto)
  2. 服務(wù)端接收到請求后繁仁,向數(shù)據(jù)庫查詢數(shù)據(jù),沒有數(shù)據(jù)時(shí)归园,進(jìn)入一個(gè)定時(shí)器查詢黄虱,有數(shù)據(jù)返回?zé)o數(shù)據(jù)則等到客戶端超時(shí)發(fā)起下一次輪詢請求
  3. 無數(shù)據(jù)返回時(shí),客戶端超時(shí)后再發(fā)起請求渺尘,有數(shù)據(jù)返回處理完數(shù)據(jù)权她,改變參數(shù)后發(fā)起一個(gè)新的請求猾编。

這里是 <a id="myexamplecode">我的</a> 代碼

//client.js 輪詢監(jiān)聽最新的消息
// url : '/api/msg/new/' + lastid (索引,建議存儲在 sessionStorage 中朱灿。

  function listeningNewMsg() {
    let lastId = sessionStorage.getItem('lastId')
    let fetchUrl = `/api/msg/new/${lastId}`
    let xhr = new XMLHttpRequest()
    xhr.onreadystatechange = () => {
      if (xhr.readyState== 4) {
        if (xhr.status == 200) {
          let res = JSON.parse(xhr.responseText)
          //... dosomething
          let newId = getNewId() //這里根據(jù)你的實(shí)際情況改變下一次請求的參數(shù)
          sessionStorage.setItem('lastId', newId)
          xhr = null //回收
          listeningNewMsg()
        }
      }
    }
    xhr.onerror = (err) => {
      console.error(err)
      return;
    }
    xhr.ontimeout = () => {
      xhr = null
      listeningNewMsg()
    }
    xhr.timeout = 20000
    xhr.open('GET', fetchUrl)
    xhr.setRequestHeaders('Accept', 'application/json, text/plain')
    xhr.send()
  }

監(jiān)聽地址使用了我自己理解的 Restful 設(shè)計(jì),具體的自行設(shè)計(jì)
下面是服務(wù)端的代碼钠四,一個(gè)路由的中間件

//server
// nodeSql.fetchNewMsg  數(shù)據(jù)庫查詢方法 @return Promise
let timer = null
const ListeningMsg = async (ctx, next) => {
  let lastId = ctx.params.lastId
  let result = await nodeSql.fetchNewMsg(lastId)
  if ( result.length <= 0 ) {
    result = await longPollingNewMsg(lastId)
  }
  if ( timer ) {
    clearTimeout(timer)
  }
  ctx.status = 200
  ctx.type = 'application/json'
  ctx.body = result
}

function longPollingNewMsg (lastId) {
  return nodeSql.fetchNewMsg(lastId)
    .then(result=>{
      if (result.length > 0) {
        return result
      } else {
        return Promise.reject('no new')
      }
    })
    .catch(err=> {
      console.log(err) // 'no new'
      return new Promise(resolve => {
        timer = setTimeout(() => resolve(), 500)
      })
      .then(()=>longPollingNewMsg(lastId))
    })
}

這樣代碼就能工作了盗扒。
結(jié)語


這段代碼中最主要的是 longPollingNewMsg 方法,里面涉及了 promise 遞歸自身的問題形导。一直以來對 promise 的返回值都沒有特別明確的概念环疼,return 到底返回的是什么東西自己一直沒有特別清晰。經(jīng)過這次代碼的驗(yàn)證朵耕,對 promise 多了幾點(diǎn)認(rèn)識

  1. 在 promise 函數(shù)中執(zhí)行 return 炫隶,返回值會被下一個(gè) then 方法接收,如果沒有 then 方法阎曹,則返回到外部伪阶。
  2. 執(zhí)行 promise 函數(shù)應(yīng)該遵循 promise 的概念, 執(zhí)行一段代碼后只有兩種結(jié)果处嫌,要么是 resolved栅贴, 要么是 rejected 。
  3. 如果需要遞歸執(zhí)行 promise 函數(shù)熏迹,應(yīng)當(dāng)先結(jié)束當(dāng)前 promise (修改其 pending 狀態(tài)為 rejected)檐薯,在 catch 代碼中繼續(xù)調(diào)用

說的不對的地方請多多指教,謝謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市坛缕,隨后出現(xiàn)的幾起案子墓猎,更是在濱河造成了極大的恐慌,老刑警劉巖赚楚,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毙沾,死亡現(xiàn)場離奇詭異,居然都是意外死亡宠页,警方通過查閱死者的電腦和手機(jī)左胞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來举户,“玉大人烤宙,你說我怎么就攤上這事×舱” “怎么了门烂?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兄淫。 經(jīng)常有香客問我屯远,道長,這世上最難降的妖魔是什么捕虽? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任慨丐,我火速辦了婚禮,結(jié)果婚禮上泄私,老公的妹妹穿的比我還像新娘房揭。我一直安慰自己,他們只是感情好晌端,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布捅暴。 她就那樣靜靜地躺著,像睡著了一般咧纠。 火紅的嫁衣襯著肌膚如雪蓬痒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天漆羔,我揣著相機(jī)與錄音梧奢,去河邊找鬼。 笑死演痒,一個(gè)胖子當(dāng)著我的面吹牛亲轨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鸟顺,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惦蚊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蹦锋,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤曾撤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后晕粪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渐裸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年巫湘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片昏鹃。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尚氛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出洞渤,到底是詐尸還是另有隱情阅嘶,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布载迄,位于F島的核電站讯柔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏护昧。R本人自食惡果不足惜魂迄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望惋耙。 院中可真熱鬧捣炬,春花似錦、人聲如沸绽榛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灭美。三九已至推溃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冲粤,已是汗流浹背美莫。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梯捕,地道東北人厢呵。 一個(gè)月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像傀顾,于是被迫代替她去往敵國和親襟铭。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容