異步編程

1. 同步模式、異步模式彤敛、回調(diào)函數(shù)

JavaScript 單線程

  • JavaScript設(shè)計初衷,運行在瀏覽器端的腳本語言臊泌,DOM操作實現(xiàn)交互
  • 不使用單線程鲤桥,會造成嚴(yán)重的線程同步問題
  • 例如:一個線程刪除,一個線程修改

JavaScript 同步模式 【Synchronous】

  • 代碼逐行運行
  • 會出現(xiàn)阻塞【某行代碼運行時間過長渠概,后面代碼會一直等待】

JavaScript 異步模式 【Asynchromous】

  • 需要進行異步執(zhí)行的代碼,開啟之后就會繼續(xù)執(zhí)行主線程代碼嫂拴,不會進行等待
  • 后序邏輯播揪,一般會通過回調(diào)函數(shù)的方式定義
  • JavaScript 是單線程的,瀏覽器不是單線程的
  • 類似于 setTimeout 之類的api筒狠,是有一個單獨的線程去執(zhí)行猪狈,等待的

<font color="red">回調(diào)函數(shù)</font>

所有異步編程方案的根基

  • http://www.reibang.com/p/40e459cfdc6f
  • 將函數(shù)作為參數(shù)
  • 我知道執(zhí)行的相關(guān)代碼,但我不知道何時能得到結(jié)果【開始調(diào)用】
  • 所以辩恼,將步驟寫好【回調(diào)函數(shù)】雇庙,交給函數(shù)運行者,運行完后會幫我調(diào)用回調(diào)函數(shù)的內(nèi)容
function foo(callback) {
    setTimeout(function() {
        callback()
    }, 3000)
}

foo(function() {
    console.log('這就是一個回調(diào)函數(shù)')
    console.log('調(diào)用者定義這個函數(shù)灶伊,執(zhí)行者執(zhí)行這個函數(shù)')
    console.log('其實就是調(diào)用者告訴執(zhí)行者異步任務(wù)結(jié)束后應(yīng)該做什么')
})

2. 回調(diào)函數(shù) Promise

異步模式

  • 運行環(huán)境提供的API是以同步或異步模式的方式工作

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

定義:由調(diào)用者定義疆前,交給執(zhí)行者執(zhí)行的函數(shù)

所有異步編程方案的根基

  • http://www.reibang.com/p/40e459cfdc6f
  • 將函數(shù)作為參數(shù)
  • 我知道執(zhí)行的相關(guān)代碼,但我不知道何時能得到結(jié)果【開始調(diào)用】
  • 所以聘萨,將步驟寫好【回調(diào)函數(shù)】竹椒,交給函數(shù)運行者,運行完后會幫我調(diào)用回調(diào)函數(shù)的內(nèi)容

Promise

  • 在ES2015中被標(biāo)準(zhǔn)化米辐,成為語言規(guī)范
graph LR

Promise2-->B[Pending]

B-->C[Fulfilled]
B-->D[Rejected]

C-->onFulfilled
D-->onRejected

Promise的基本用法

const promise = new Promise(function(resolve, reject) {
  // 這里用于"兌現(xiàn)"承諾
  resolve(100) // 承諾達(dá)成

  reject(new Error('promise rejected')) // 承諾失敗

  // 兩者只能調(diào)用其一

})

promise.then(function(value) {
  console.log('resolved', value)
}, function(error) {
  console.log('rejected', error)
})
console.log('end')

Promise 方式的 ajax
function ajax(url) {
  return new Promise(function(resolve, reject) {
    
    var xhr = new XMLHttpRequest()
    xhr.open('GET', url)
    xhr.responseType = 'json'
    xhr.onload = function() {
      if (this.status == 200) {
        resolve(this.response)
      } else {
        reject(new Error(this.statusText))
      }
    }
    xhr.send()
  })
}

ajax('/api/users.json').then(function(res) {
  console.log(res)
}, function(error) {
  console.log(error)
})
使用Promise的鏈?zhǔn)秸{(diào)用盡可能保證異步任務(wù)扁平化
  • Promise對象的then方法會返回一個全新的Promise對象
  • 后面的then方法就是在為上一個then返回的Promise注冊回調(diào)
  • 前面then方法中回調(diào)函數(shù)的返回值會作為后面then方法回調(diào)的參數(shù)
  • 如果回調(diào)中返回的是Promise, 好后面then方法的回調(diào)會等待它的結(jié)束
Promise的異常處理
ajax('/api/users.json')
    .then()
    .catch()
    
// .catch() == .then(undefine, () => {
    // catch后聽處理
})

全局能監(jiān)聽 unhandledrejection

  • 不建議在全局監(jiān)聽
// 瀏覽器中
window.addEventListener('unhandledrejection', event => {
    const { reason, promise } = event
    console.log(reason, promise)
    // reason => Promise 失敗原因胸完,一般是一個錯誤對象
    // promise => 出現(xiàn)異常的 Promise 對象
    
    event.preventDefault()
}, false)

// 瀏覽器中
process.on('unhandledrejection', (reason, promise) => {
    console.log(reason, promise)
    // reason => Promise 失敗原因书释,一般是一個錯誤對象
    // promise => 出現(xiàn)異常的 Promise 對象
})
Promise的靜態(tài)方法
  • Promise.resolve()
Promise.resolve('foo')
    .then(function(value) {
        console.log(value)
    })
    
// 等價于
new Promise(function(resolve, reject) {
    resolve('foo')
})


var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) // true


Promise.resolve({
  then: function (onFulfilled, onRejected) {
    onFulfilled('foo')
  }
})

  • Promise.reject()
Promise.reject('anything')
  .catch(function(error) {
    console.log(error)
  })
Promise 并行執(zhí)行
  • Promise.all 等待所有任務(wù)結(jié)束
var promise = Promise.all([
  ajax('/api/users.json'),
  ajax('/api/posts.json')
])

promise.then(function(values) {
  console.log(values)
}).catch(function(error) {
  console.log(error)
})
  • Promise.race 只會等待第一個結(jié)束的任務(wù)
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
  setTimeout(() => reject(new Error('timeout')), 500)
})

Promise.race([
  request,
  timeout
])
  .then(value => {
    console.log(value)
  })
  .catch(error => {
    console.log(error)
  })

3.1 異步方案 Generator

  • ES2015提供的 Generator

生成器函數(shù)回顧

// 生成器函數(shù)回顧
function *foo() {
  console.log('start')

  try {
    const res = yield 'foo'
    console.log(res) // bar
  } catch(e) {
    console.log(e)
  }
}

const generator = foo()

const result = generator.next()
console.log(result)

generator.next('bar')

generator.throw(new Error('Generator error')) // 就需要 foo當(dāng)中try catch

體驗Generator函數(shù)異步方案

function *main() {
  const users = yield ajax('/api/users.json')
  console.log(users)

  const posts = yield ajax('/api/posts.json')
  console.log(posts)
}

const g = main()
const result = g.next()
result.value.then(data => {
  const result2 = g.next(data) // 在這里傳入 上面 main中的users才會有值
  if (result2.done) return

  result2.value.then(data => {
    g.next(data)

  })
})

遞歸執(zhí)行Generator函數(shù)


function *main() {
  const users = yield ajax('/api/users.json')
  console.log(users)

  const posts = yield ajax('/api/posts.json')
  console.log(posts)
}
const g = main()

co(main)

function co(generator) {
  const g = generator()

  function handleResult(result) {
    if (result.done) return // 生成器函數(shù)結(jié)束
    result.value.then(data => {
      handleResult(g.next(data))
    }).catch(error => {
      g.throw(error)
    })
  }
  handleResult(g.next())
}

3.2 異步方案 Async

Anync / Await 語法糖

語言層面的異步編程標(biāo)準(zhǔn)

async function main() {
  try {
    const users = await ajax('/api/users.json')
    console.log(users)

    const posts = await ajax('/api/posts.json')
    console.log(posts)
  } catch(e) {
    console.log(e)
  }
}

const promise = main()
promise.then(() => {
  console.log('all completed')
})
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爆惧,一起剝皮案震驚了整個濱河市锨能,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌腹侣,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件傲隶,死亡現(xiàn)場離奇詭異,居然都是意外死亡跺株,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門乒省,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袖扛,你說我怎么就攤上這事∏猓” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵惨篱,是天一觀的道長。 經(jīng)常有香客問我砸讳,道長,這世上最難降的妖魔是什么簿寂? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮陶耍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己坤按,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布臭脓。 她就那樣靜靜地躺著,像睡著了一般来累。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窘奏,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機與錄音领猾,去河邊找鬼。 笑死骇扇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的少孝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼袁翁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了婿脸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎凿歼,沒想到半個月后褪迟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體答憔,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年虐拓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡揪利,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疟位,到底是詐尸還是另有隱情,我是刑警寧澤喘垂,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站得院,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏章贞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一阱驾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧里覆,春花似錦、人聲如沸喧枷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至戚扳,卻和暖如春忧便,著一層夾襖步出監(jiān)牢的瞬間帽借,已是汗流浹背珠增。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工砍艾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人脆荷。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓懊悯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炭分。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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