異步編程

什么是同步和異步?

你可能知道, JavaScript 語(yǔ)言 的執(zhí)行環(huán)境是“單線(xiàn)程”

所謂“單線(xiàn)程”, 就是指一次只能完成一件任務(wù), 如果有多個(gè)任務(wù), 就必須排隊(duì), 前面一個(gè)任務(wù)完成, 再執(zhí)行后面一個(gè)任務(wù), 以此類(lèi)推
例如現(xiàn)實(shí)生活中的排隊(duì)

這種模式的好處是實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單, 執(zhí)行環(huán)境相對(duì)單純, 壞處是只要有一個(gè)任務(wù)耗時(shí)很長(zhǎng), 后面的任務(wù)都必須排隊(duì)等著, 會(huì)拖延整個(gè)程序的執(zhí)行
常見(jiàn)的瀏覽器無(wú)響應(yīng)(假死), 往往就是因?yàn)槟骋欢?JavaScript 代碼長(zhǎng)時(shí)間運(yùn)行(比如死循環(huán)), 導(dǎo)致整個(gè)頁(yè)面卡在這個(gè)地方, 其他任務(wù)無(wú)法執(zhí)行

為了解決這個(gè)問(wèn)題, JavaScript 語(yǔ)言將任務(wù)的執(zhí)行模式分成兩種

  • 同步(Synchronous)
  • 異步(Asynchronous)

這里的 “同步”和“異步” 與我們現(xiàn)實(shí)中的同步膜钓、異步恰恰相反

例如:

  • 一邊吃飯一邊打電話(huà), 我們認(rèn)為這是同時(shí)進(jìn)行(同步執(zhí)行)的, 但在計(jì)算機(jī)中, 這種行為叫做異步執(zhí)行
  • 吃飯的同時(shí), 必須吃完飯才能打電話(huà), 我們認(rèn)為這是不能同時(shí)進(jìn)行(異步執(zhí)行)的, 但在計(jì)算機(jī)中, 這種行為我們叫做同步執(zhí)行

至于為什么, 那你要問(wèn)英文單詞了, 例如 異步(Asynchronous) 翻譯成中文是異步的, 但在計(jì)算機(jī)中, 表示的是我們認(rèn)知的同時(shí)執(zhí)行的

什么時(shí)候我們需要異步處理事件?

  • 一種很常見(jiàn)的場(chǎng)景自然就是網(wǎng)絡(luò)請(qǐng)求了
  • 我們封裝一個(gè)網(wǎng)絡(luò)請(qǐng)求的函數(shù), 因?yàn)椴荒芰⒓茨玫浇Y(jié)果, 所以不能像簡(jiǎn)單的 3 + 4 = 7 一樣立刻獲得結(jié)果
  • 所以我們往往會(huì)傳入另一個(gè)函數(shù) (回調(diào)函數(shù) callback), 在數(shù)據(jù)請(qǐng)求成功之后, 再將得到的數(shù)據(jù)以參數(shù)的形式傳遞給回調(diào)函數(shù)

JavaScript 和 Node.js 中的異步操作都會(huì)在最后執(zhí)行, 例如 ajax、readFile肠阱、writeFile、setTimeout 等

獲取異步操作的值只能使用回調(diào)函數(shù)的方式, 異步操作都是最后執(zhí)行

回調(diào)函數(shù)

回調(diào)函數(shù)的方式獲取異步操作內(nèi)的數(shù)據(jù)

function sum(a, b, callback) {
  console.log(1)
  setTimeout(function () {
    callback(a + b)
  }, 1000)
  console.log(2)
}

sum(10, 20, function (res) {
  console.log(res)
})

// log: 1 2 30

這種方式雖然看似沒(méi)什么問(wèn)題, 但是, 當(dāng)網(wǎng)絡(luò)請(qǐng)求非常復(fù)雜時(shí), 就會(huì)出現(xiàn)回調(diào)地獄

ok, 我們用一個(gè)非巢犯撸夸張的案例來(lái)說(shuō)明

$.ajax('url1', function (data1) {
  $.ajax(data1['url2'], function (data2) {
    $.ajax(data2['url3'], function (data3) {
      $.ajax(data3['url4'], function (data4) {
        console.log(data4)
      })
    })
  })
})
  • 我們需要通過(guò)一個(gè) url1 向服務(wù)器請(qǐng)求一個(gè)數(shù)據(jù) data1, data1 中又包含了下一個(gè)請(qǐng)求的 url2
  • 我們需要通過(guò)一個(gè) url2 向服務(wù)器請(qǐng)求一個(gè)數(shù)據(jù) data2, data2 中又包含了下一個(gè)請(qǐng)求的 url3
  • 我們需要通過(guò)一個(gè) url3 向服務(wù)器請(qǐng)求一個(gè)數(shù)據(jù) data3, data3 中又包含了下一個(gè)請(qǐng)求的 url4
  • 發(fā)送網(wǎng)絡(luò)請(qǐng)求 url4, 獲取最終的數(shù)據(jù) data4

上面的代碼有什么問(wèn)題?

  • 正常情況下, 不會(huì)有什么問(wèn)題, 可以正常運(yùn)行并且獲取我們想要的數(shù)據(jù)
  • 但是, 這樣的代碼閱讀性非常差, 而且非常不利于維護(hù)
  • 如果有多個(gè)異步同時(shí)執(zhí)行, 無(wú)法確認(rèn)他們的執(zhí)行順序, 所以通過(guò)嵌套的方式能保證代碼的執(zhí)行順序問(wèn)題
  • 我們更加期望的是一種更加優(yōu)雅的方式來(lái)進(jìn)行這種異步操作

Promise

什么是 Promise ?

ES6 中有一個(gè)非常重要和好用的特性就是 Promise

Promise 到底是做什么的?

  • Promise 是異步編程的一種解決方案, 比傳統(tǒng)的解決方案回調(diào)函數(shù)和事件更合理和更強(qiáng)大

所謂 Promise, 簡(jiǎn)單說(shuō)就是一個(gè)容器, 里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果

為了解決回調(diào)地獄所帶來(lái)的問(wèn)題, ES6 里引進(jìn)了 Promise, 有了 Promise 對(duì)象, 就可以將異步操作以同步操作的流程表達(dá)出來(lái), 避免了層層嵌套的回調(diào)函數(shù)
Promise 對(duì)象提供統(tǒng)一的接口, 使得控制異步操作更加容易

Promise 的特點(diǎn)

Promise 對(duì)象有以下兩個(gè)特點(diǎn)

  1. 對(duì)象的狀態(tài)不受外界影響, Promise 對(duì)象代表一個(gè)異步操作, 有三種狀態(tài): pending(進(jìn)行中)倘屹、fulfill(已成功) 和 rejected(已失敗), 只有異步操作的結(jié)果, 可以決定當(dāng)前是哪一種狀態(tài), 任何其他操作都無(wú)法改變這個(gè)狀態(tài), 這也是 Promise 這個(gè)名字的由來(lái), 它的英語(yǔ)意思就是 “承諾”, 表示其他手段無(wú)法改變
  2. 一旦狀態(tài)改變, 就不會(huì)再變, 任何時(shí)候都可以得到這個(gè)結(jié)果, Promise 對(duì)象的狀態(tài)改變, 只有兩種可能: 從 pending 變?yōu)?fulfill從 pending 變?yōu)?rejected, 只要這兩種情況發(fā)生, 狀態(tài)就凝固了, 不會(huì)再發(fā)生改變, 會(huì)一直保持這個(gè)結(jié)果, 這時(shí)就稱(chēng)為 resolved(已定型), 如果改變已經(jīng)發(fā)生了, 你再對(duì) Promise 對(duì)象添加回調(diào)函數(shù), 也會(huì)立即得到這個(gè)結(jié)果, 這與事件(Event)完全不同, 事件的特點(diǎn)是, 如果你錯(cuò)過(guò)了它, 再去監(jiān)聽(tīng), 是得不到結(jié)果的

Promise 的缺點(diǎn)

  • 首先, 無(wú)法取消 Promise, 一旦新建它就會(huì)立即執(zhí)行, 無(wú)法中途取消
  • 其次, 如果不設(shè)置回調(diào)函數(shù), Promise 內(nèi)部拋出的錯(cuò)誤, 不會(huì)反應(yīng)到外部
  • 第三, 當(dāng)處于 pending 狀態(tài)時(shí), 無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開(kāi)始還是即將完成)

Promise 的三種狀態(tài)

  • pending : 等待(wait)狀態(tài), 比如正在進(jìn)行網(wǎng)絡(luò)請(qǐng)求, 或者定時(shí)器沒(méi)有到時(shí)間
  • fulfilled : 滿(mǎn)足狀態(tài), 當(dāng)我們主動(dòng)調(diào)用 resolve 時(shí), 就處于該狀態(tài), 并且回調(diào) .then()
  • rejected : 拒絕狀態(tài), 當(dāng)我們主動(dòng)調(diào)用 reject 時(shí), 就處于該狀態(tài), 并且回調(diào) .catch()

Promise 基本用法

ES6 規(guī)定, Promise 對(duì)象是一個(gè)構(gòu)造函數(shù), 用來(lái)生成 Promise 實(shí)例

new Promise((resolve, reject) => {
  // ... 某些異步代碼

  if (/* 異步操作成功 */){
    resolve(data);  // data 里是異步執(zhí)行后的返回值
  } else {
    reject(error);  // error 里是異步執(zhí)行錯(cuò)誤后的錯(cuò)誤信息
  }
}).then(data => {
  // 這里對(duì) data 就可以進(jìn)行數(shù)據(jù)拿取操作了
  console.log('success')
}).catch(error => {
  console.log('failure')
})

Promise 構(gòu)造函數(shù)接受一個(gè)函數(shù)作為參數(shù), 該函數(shù)的兩個(gè)參數(shù)分別是 resolve 和 reject
它們是兩個(gè)函數(shù), 由 JavaScript 引擎提供, 不需要自己部署

resolve

  • resolve 函數(shù)的作用是將 Promise 對(duì)象的狀態(tài)從 “未完成”變?yōu)椤俺晒Α?即從 pending 變?yōu)?fulfilled), 在異步操作成功時(shí)調(diào)用, 并將異步操作的結(jié)果, 作為參數(shù)傳遞出去

reject

  • reject 函數(shù)的作用是將 Promise 對(duì)象的狀態(tài)從 “未完成”變?yōu)椤笆 ?即從 pending 變?yōu)?rejected), 在異步操作失敗時(shí)調(diào)用, 并將異步操作報(bào)出的錯(cuò)誤, 作為參數(shù)傳遞出去

then 方法還可以接受兩個(gè)回調(diào)函數(shù)作為參數(shù), 合并 .catch()

promise.then(data => { 
  // 這里對(duì) data 就可以進(jìn)行數(shù)據(jù)拿取操作了
  console.log('success')
}, error => {
  console.log('failure')
})
  • 第一個(gè)回調(diào)函數(shù)是 Promise 對(duì)象的狀態(tài)變?yōu)?fulfilled 時(shí)調(diào)用
  • 第二個(gè)回調(diào)函數(shù)是 Promise 對(duì)象的狀態(tài)變?yōu)?rejected 時(shí)調(diào)用
  • 其中, 第二個(gè)回調(diào)函數(shù)是可選的, 不一定要提供, 這兩個(gè)函數(shù)都接受Promise 對(duì)象傳出的值作為參數(shù)

一般來(lái)說(shuō), 調(diào)用 resolve 或 reject 以后, Promise 的使命就完成了, 后繼操作應(yīng)該放到 then 方法里面, 而不應(yīng)該直接寫(xiě)在 resolve 或 reject 的后面
所以, 最好在將它們加上 return 語(yǔ)句, 這樣就不會(huì)有意外

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的語(yǔ)句不會(huì)執(zhí)行
  console.log(2);
})

Promise 鏈?zhǔn)秸{(diào)用

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success1')
  }, 1000)
}).then(res => {
  console.log(res)  // success1
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success2')
    }, 1000)
  })
}).then(res => {
  console.log(res)  // success2
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('success3')
    }, 1000)
  })
}).then(res => {
  console.log(res)  // success3
})

Promise 鏈?zhǔn)秸{(diào)用簡(jiǎn)寫(xiě)

如果我們希望數(shù)據(jù)直接包裝成 Promise.resolve, 那么在 then 中可以直接返回?cái)?shù)據(jù)

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success1')
  }, 1000)
}).then(res => {
  console.log(res)  // success1
  return 'success2'
}).then(res => {
  console.log(res)  // success2
  return 'success3'
}).then(res => {
  console.log(res)  // success3
})

Promise.prototype.finally()

finally()方法用于指定不管 Promise 對(duì)象最后狀態(tài)如何, 都會(huì)執(zhí)行的操作

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代碼中, 不管promise最后的狀態(tài), 在執(zhí)行完thencatch指定的回調(diào)函數(shù)以后, 都會(huì)執(zhí)行finally方法指定的回調(diào)函數(shù)

finally方法的回調(diào)函數(shù)不接受任何參數(shù), 這意味著沒(méi)有辦法知道前面的 Promise 狀態(tài)到底是fulfilled還是rejected, 這表明, finally方法里面的操作, 應(yīng)該是與狀態(tài)無(wú)關(guān)的, 不依賴(lài)于 Promise 的執(zhí)行結(jié)果

Promise.all()

Promise.all()方法用于將多個(gè) Promise 實(shí)例, 包裝成一個(gè)新的 Promise 實(shí)例

const p = Promise.all([p1, p2])

上面代碼中, Promise.all()方法接受一個(gè)數(shù)組作為參數(shù), p1记劝、p2都是 Promise 實(shí)例, Promise.all()方法的參數(shù)可以不是數(shù)組, 但必須具有 Iterator 接口, 且返回的每個(gè)成員都是 Promise 實(shí)例

p的狀態(tài)由p1p2決定, 分成兩種情況

  1. 只有p1吕粹、p2的狀態(tài)都變成fulfilled, p的狀態(tài)才會(huì)變成fulfilled, 此時(shí)p1种柑、p2的返回值組成一個(gè)數(shù)組, 傳遞給p的回調(diào)函數(shù)
  2. 只要p1p2之中有一個(gè)被rejected, p的狀態(tài)就變成rejected, 此時(shí)第一個(gè)被reject的實(shí)例的返回值, 會(huì)傳遞給p的回調(diào)函數(shù)
/* 兩個(gè)異步操作狀態(tài)都為 fulfilled */
var p1 = new Promise((resolve, reject) => {
  resolve('request1')
})

var p2 = new Promise((resolve, reject) => {
  resolve('request2')
})

Promise.all([p1, p2])
  .then(res => console.log(res)) // ['request1', 'request2']
  .catch(e => console.log(e))

/* 其中有一個(gè)異步操作狀態(tài)為 rejected */
var p1 = new Promise((resolve, reject) => {
  resolve('request1')
})

var p2 = new Promise((resolve, reject) => {
  reject('request2 error')
})

Promise.all([p1, p2])
  .then(res => console.log(res))
  .catch(e => console.log(e)) // 'request2 error'

注意, 如果作為參數(shù)的 Promise 實(shí)例, 自己定義了catch方法, 那么它一旦被rejected, 并不會(huì)觸發(fā)Promise.all()catch方法

const p1 = new Promise((resolve, reject) => {
  resolve('request1')
})

const p2 = new Promise((resolve, reject) => {
  throw new Error('報(bào)錯(cuò)了')
}).catch(e => e)

Promise.all([p1, p2])
  .then(res => console.log(res)) // ['request1', Error: 報(bào)錯(cuò)了]
  .catch(e => console.log(e))

上面代碼中, p1 會(huì) resolved, p2 首先會(huì) rejected, 但是 p2 有自己的catch方法, 該方法返回的是一個(gè)新的 Promise 實(shí)例, p2 指向的實(shí)際上是這個(gè)實(shí)例

該實(shí)例執(zhí)行完catch方法后, 也會(huì)變成 resolved, 導(dǎo)致Promise.all()方法參數(shù)里面的兩個(gè)實(shí)例都會(huì)resolved, 因此會(huì)調(diào)用then方法指定的回調(diào)函數(shù), 而不會(huì)調(diào)用catch方法指定的回調(diào)函數(shù)

如果 p2 沒(méi)有自己的catch方法, 就會(huì)調(diào)用Promise.all()catch方法

Promise.race()

Promise.race()方法同樣是將多個(gè) Promise 實(shí)例, 包裝成一個(gè)新的 Promise 實(shí)例

const p = Promise.race([p1, p2])
  • 只要p1匹耕、p2之中有一個(gè)實(shí)例率先改變狀態(tài), p的狀態(tài)就跟著改變

  • 那個(gè)率先改變的 Promise 實(shí)例的返回值, 就傳遞給p的回調(diào)函數(shù)

  • Promise.race()方法的參數(shù)與Promise.all()方法一樣

下面是一個(gè)例子

/* 第一個(gè)異步操作率先完成, 并且狀態(tài)為 fulfilled */
Promise.race([
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request success')
    }, 1000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('request timeout')
    }, 2000)
  })
])
  .then(res => console.log(res))  // request success
  .catch(e => console.log(e))

/* 第二個(gè)異步操作先完成, 并且狀態(tài)為 rejected */
Promise.race([
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request success')
    }, 1000)
  }),
  new Promise((resolve, reject) => {
    setTimeout(() => {
      reject('request timeout')
    }, 500)
  })
])
  .then(res => console.log(res))
  .catch(e => console.log(e)) // request timeout
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末莹规,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子泌神,更是在濱河造成了極大的恐慌良漱,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欢际,死亡現(xiàn)場(chǎng)離奇詭異母市,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)损趋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)患久,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事蒋失》蹬粒” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,960評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵篙挽,是天一觀的道長(zhǎng)荆萤。 經(jīng)常有香客問(wèn)我,道長(zhǎng)铣卡,這世上最難降的妖魔是什么链韭? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,750評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮煮落,結(jié)果婚禮上敞峭,老公的妹妹穿的比我還像新娘。我一直安慰自己蝉仇,他們只是感情好旋讹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著轿衔,像睡著了一般沉迹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呀枢,一...
    開(kāi)封第一講書(shū)人閱讀 51,604評(píng)論 1 305
  • 那天胚股,我揣著相機(jī)與錄音笼痛,去河邊找鬼裙秋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缨伊,可吹牛的內(nèi)容都是我干的摘刑。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼刻坊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼枷恕!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起谭胚,我...
    開(kāi)封第一講書(shū)人閱讀 39,253評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤徐块,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后灾而,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胡控,經(jīng)...
    沈念sama閱讀 45,702評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評(píng)論 3 336
  • 正文 我和宋清朗相戀三年旁趟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昼激。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖橙困,靈堂內(nèi)的尸體忽然破棺而出瞧掺,到底是詐尸還是另有隱情,我是刑警寧澤凡傅,帶...
    沈念sama閱讀 35,734評(píng)論 5 346
  • 正文 年R本政府宣布辟狈,位于F島的核電站,受9級(jí)特大地震影響像捶,放射性物質(zhì)發(fā)生泄漏上陕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評(píng)論 3 330
  • 文/蒙蒙 一拓春、第九天 我趴在偏房一處隱蔽的房頂上張望释簿。 院中可真熱鬧,春花似錦硼莽、人聲如沸庶溶。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,934評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)偏螺。三九已至,卻和暖如春匆光,著一層夾襖步出監(jiān)牢的瞬間套像,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,052評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工终息, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留夺巩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評(píng)論 3 371
  • 正文 我出身青樓周崭,卻偏偏與公主長(zhǎng)得像柳譬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子续镇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評(píng)論 2 355

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

  • 前面的話(huà) JS有很多強(qiáng)大的功能美澳,其中一個(gè)是它可以輕松地搞定異步編程。作為一門(mén)為Web而生的語(yǔ)言摸航,它從一開(kāi)始就需要能...
    CodeMT閱讀 615評(píng)論 0 0
  • 最近抽空復(fù)習(xí)了一下之前讀過(guò)的JS書(shū)制跟,看了一下關(guān)于回調(diào)函數(shù)和promise相關(guān)部分。 回調(diào)函數(shù) 提到異步編程酱虎,盡管發(fā)...
    _遠(yuǎn)方?jīng)]有詩(shī)閱讀 620評(píng)論 0 0
  • 參考鏈接:學(xué)習(xí)RxJS: 導(dǎo)入http://www.moye.me/2016/05/31/learning_rxj...
    sy隨緣閱讀 2,816評(píng)論 0 1
  • 一雨膨、Javascript實(shí)現(xiàn)異步編程的過(guò)程以及原理 1、為什么要用Javascript異步編程 眾所周知逢净,Java...
    Ebony_7c03閱讀 864評(píng)論 0 2
  • 前言 編程語(yǔ)言很多的新概念都是為了更好的解決老問(wèn)題而提出來(lái)的哥放。這篇博客就是一步步分析異步編程解決方案的問(wèn)題以及后續(xù)...
    李向_c52d閱讀 1,068評(píng)論 0 2