JS 異步編程六種方案

前言

我們知道Javascript語言的執(zhí)行環(huán)境是"單線程"盒粮。也就是指一次只能完成一件任務(wù)。如果有多個(gè)任務(wù)苦丁,就必須排隊(duì)碟案,前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù)笛丙。

這種模式雖然實(shí)現(xiàn)起來比較簡單漾脂,執(zhí)行環(huán)境相對(duì)單純,但是只要有一個(gè)任務(wù)耗時(shí)很長胚鸯,后面的任務(wù)都必須排隊(duì)等著骨稿,會(huì)拖延整個(gè)程序的執(zhí)行。常見的瀏覽器無響應(yīng)(假死)蠢琳,往往就是因?yàn)槟骋欢蜫avascript代碼長時(shí)間運(yùn)行(比如死循環(huán))啊终,導(dǎo)致整個(gè)頁面卡在這個(gè)地方,其他任務(wù)無法執(zhí)行傲须。

為了解決這個(gè)問題蓝牲,Javascript語言將任務(wù)的執(zhí)行模式分成兩種:同步和異步。本文主要介紹異步編程幾種辦法泰讽,并通過比較例衍,得到最佳異步編程的解決方案!

一已卸、同步與異步

我們可以通俗理解為異步就是一個(gè)任務(wù)分成兩段佛玄,先執(zhí)行第一段,然后轉(zhuǎn)而執(zhí)行其他任務(wù)累澡,等做好了準(zhǔn)備梦抢,再回過頭執(zhí)行第二段。排在異步任務(wù)后面的代碼愧哟,不用等待異步任務(wù)結(jié)束會(huì)馬上運(yùn)行奥吩,也就是說哼蛆,異步任務(wù)不具有”堵塞“效應(yīng)。比如霞赫,有一個(gè)任務(wù)是讀取文件進(jìn)行處理腮介,異步的執(zhí)行過程就是下面這樣

這種不連續(xù)的執(zhí)行,就叫做異步端衰。相應(yīng)地叠洗,連續(xù)的執(zhí)行,就叫做同步

"異步模式"非常重要旅东。在瀏覽器端灭抑,耗時(shí)很長的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng)玉锌,最好的例子就是Ajax操作名挥。在服務(wù)器端,"異步模式"甚至是唯一的模式主守,因?yàn)閳?zhí)行環(huán)境是單線程的禀倔,如果允許同步執(zhí)行所有http請(qǐng)求,服務(wù)器性能會(huì)急劇下降参淫,很快就會(huì)失去響應(yīng)救湖。接下來介紹下異步編程六種方法。

二涎才、回調(diào)函數(shù)(Callback)

回調(diào)函數(shù)是異步操作最基本的方法鞋既。以下代碼就是一個(gè)回調(diào)函數(shù)的例子:

ajax(url, () => {
    // 處理邏輯
})

但是回調(diào)函數(shù)有一個(gè)致命的弱點(diǎn),就是容易寫出回調(diào)地獄(Callback hell)耍铜。假設(shè)多個(gè)請(qǐng)求存在依賴性邑闺,你可能就會(huì)寫出如下代碼:

ajax(url, () => {
    // 處理邏輯
    ajax(url1, () => {
        // 處理邏輯
        ajax(url2, () => {
            // 處理邏輯
        })
    })
})

回調(diào)函數(shù)的優(yōu)點(diǎn)是簡單、容易理解和實(shí)現(xiàn)棕兼,缺點(diǎn)是不利于代碼的閱讀和維護(hù)陡舅,各個(gè)部分之間高度耦合,使得程序結(jié)構(gòu)混亂伴挚、流程難以追蹤(尤其是多個(gè)回調(diào)函數(shù)嵌套的情況)靶衍,而且每個(gè)任務(wù)只能指定一個(gè)回調(diào)函數(shù)。此外它不能使用 try catch 捕獲錯(cuò)誤茎芋,不能直接 return颅眶。

三、事件監(jiān)聽

這種方式下田弥,異步任務(wù)的執(zhí)行不取決于代碼的順序涛酗,而取決于某個(gè)事件是否發(fā)生

下面是兩個(gè)函數(shù)f1和f2,編程的意圖是f2必須等到f1執(zhí)行完成煤杀,才能執(zhí)行眷蜈。首先,為f1綁定一個(gè)事件(這里采用的jQuery的寫法)

f1.on('done', f2);

上面這行代碼的意思是沈自,當(dāng)f1發(fā)生done事件,就執(zhí)行f2辜妓。然后枯途,對(duì)f1進(jìn)行改寫:

function f1() {
  setTimeout(function () {
    // ...
    f1.trigger('done');
  }, 1000);
}

上面代碼中,f1.trigger('done')表示籍滴,執(zhí)行完成后酪夷,立即觸發(fā)done事件,從而開始執(zhí)行f2孽惰。

這種方法的優(yōu)點(diǎn)是比較容易理解晚岭,可以綁定多個(gè)事件,每個(gè)事件可以指定多個(gè)回調(diào)函數(shù)勋功,而且可以"去耦合"坦报,有利于實(shí)現(xiàn)模塊化。缺點(diǎn)是整個(gè)程序都要變成事件驅(qū)動(dòng)型狂鞋,運(yùn)行流程會(huì)變得很不清晰片择。閱讀代碼的時(shí)候,很難看出主流程骚揍。

四字管、發(fā)布訂閱

我們假定,存在一個(gè)"信號(hào)中心"信不,某個(gè)任務(wù)執(zhí)行完成嘲叔,就向信號(hào)中心"發(fā)布"(publish)一個(gè)信號(hào),其他任務(wù)可以向信號(hào)中心"訂閱"(subscribe)這個(gè)信號(hào)抽活,從而知道什么時(shí)候自己可以開始執(zhí)行硫戈。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)酌壕。

首先掏愁,f2向信號(hào)中心jQuery訂閱done信號(hào)。

jQuery.subscribe('done', f2);

然后卵牍,f1進(jìn)行如下改寫:

function f1() {
  setTimeout(function () {
    // ...
    jQuery.publish('done');
  }, 1000);
}

上面代碼中果港,jQuery.publish('done')的意思是,f1執(zhí)行完成后糊昙,向信號(hào)中心jQuery發(fā)布done信號(hào)辛掠,從而引發(fā)f2的執(zhí)行。
f2完成執(zhí)行后,可以取消訂閱(unsubscribe)

jQuery.unsubscribe('done', f2);

這種方法的性質(zhì)與“事件監(jiān)聽”類似萝衩,但是明顯優(yōu)于后者回挽。因?yàn)榭梢酝ㄟ^查看“消息中心”,了解存在多少信號(hào)猩谊、每個(gè)信號(hào)有多少訂閱者千劈,從而監(jiān)控程序的運(yùn)行。

五牌捷、Promise/A+

Promise本意是承諾墙牌,在程序中的意思就是承諾我過一段時(shí)間后會(huì)給你一個(gè)結(jié)果。 什么時(shí)候會(huì)用到過一段時(shí)間暗甥?答案是異步操作喜滨,異步是指可能比較長時(shí)間才有結(jié)果的才做,例如網(wǎng)絡(luò)請(qǐng)求撤防、讀取本地文件等

1.Promise的三種狀態(tài)

  • Pending----Promise對(duì)象實(shí)例創(chuàng)建時(shí)候的初始狀態(tài)
  • Fulfilled----可以理解為成功的狀態(tài)
  • Rejected----可以理解為失敗的狀態(tài)

這個(gè)承諾一旦從等待狀態(tài)變成為其他狀態(tài)就永遠(yuǎn)不能更改狀態(tài)了虽风,比如說一旦狀態(tài)變?yōu)?resolved 后,就不能再次改變?yōu)镕ulfilled

let p = new Promise((resolve, reject) => {
  reject('reject')
  resolve('success')//無效代碼不會(huì)執(zhí)行
})
p.then(
  value => {
    console.log(value)
  },
  reason => {
    console.log(reason)//reject
  }
)

當(dāng)我們?cè)跇?gòu)造 Promise 的時(shí)候寄月,構(gòu)造函數(shù)內(nèi)部的代碼是立即執(zhí)行的

new Promise((resolve, reject) => {
  console.log('new Promise')
  resolve('success')
})
console.log('end')
// new Promise => end

2.promise的鏈?zhǔn)秸{(diào)用

  • 每次調(diào)用返回的都是一個(gè)新的Promise實(shí)例(這就是then可用鏈?zhǔn)秸{(diào)用的原因)
  • 如果then中返回的是一個(gè)結(jié)果的話會(huì)把這個(gè)結(jié)果傳遞下一次then中的成功回調(diào)
  • 如果then中出現(xiàn)異常,會(huì)走下一個(gè)then的失敗回調(diào)
  • 在 then中使用了return辜膝,那么 return 的值會(huì)被Promise.resolve() 包裝(見例1,2)
  • then中可以不傳遞參數(shù)剥懒,如果不傳遞會(huì)透到下一個(gè)then中(見例3)
  • catch 會(huì)捕獲到?jīng)]有捕獲的異常

接下來我們看幾個(gè)例子:

  // 例1
  Promise.resolve(1)
  .then(res => {
    console.log(res)
    return 2 //包裝成 Promise.resolve(2)
  })
  .catch(err => 3)
  .then(res => console.log(res))

// 例2
Promise.resolve(1)
  .then(x => x + 1)
  .then(x => {
    throw new Error('My Error')
  })
  .catch(() => 1)
  .then(x => x + 1)
  .then(x => console.log(x)) //2
  .catch(console.error)

// 例3
let fs = require('fs')
function read(url) {
  return new Promise((resolve, reject) => {
    fs.readFile(url, 'utf8', (err, data) => {
      if (err) reject(err)
      resolve(data)
    })
  })
}
read('./name.txt')
  .then(function(data) {
    throw new Error() //then中出現(xiàn)異常,會(huì)走下一個(gè)then的失敗回調(diào)
  }) //由于下一個(gè)then沒有失敗回調(diào)内舟,就會(huì)繼續(xù)往下找,如果都沒有初橘,就會(huì)被catch捕獲到
  .then(function(data) {
    console.log('data')
  })
  .then()
  .then(null, function(err) {
    console.log('then', err)// then error
  })
  .catch(function(err) {
    console.log('error')
  })

Promise不僅能夠捕獲錯(cuò)誤验游,而且也很好地解決了回調(diào)地獄的問題,可以把之前的回調(diào)地獄例子改寫為如下代碼:

ajax(url)
  .then(res => {
      console.log(res)
      return ajax(url1)
  }).then(res => {
      console.log(res)
      return ajax(url2)
  }).then(res => console.log(res))

它也是存在一些缺點(diǎn)的保檐,比如無法取消 Promise耕蝉,錯(cuò)誤需要通過回調(diào)函數(shù)捕獲。

六夜只、生成器Generators/ yield

Generator 函數(shù)是 ES6 提供的一種異步編程解決方案垒在,語法行為與傳統(tǒng)函數(shù)完全不同,Generator 最大的特點(diǎn)就是可以控制函數(shù)的執(zhí)行扔亥。

  • 語法上场躯,首先可以把它理解成,Generator 函數(shù)是一個(gè)狀態(tài)機(jī)旅挤,封裝了多個(gè)內(nèi)部狀態(tài)踢关。
  • Generator 函數(shù)除了狀態(tài)機(jī),還是一個(gè)遍歷器對(duì)象生成函數(shù)粘茄。
  • 可暫停函數(shù), yield可暫停签舞,next方法可啟動(dòng)秕脓,每次返回的是yield后的表達(dá)式結(jié)果
  • yield表達(dá)式本身沒有返回值儒搭,或者說總是返回undefined吠架。next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值搂鲫。

我們先來看個(gè)例子:

function *foo(x) {
  let y = 2 * (yield (x + 1))
  let z = yield (y / 3)
  return (x + y + z)
}
let it = foo(5)
console.log(it.next())   // => {value: 6, done: false}
console.log(it.next(12)) // => {value: 8, done: false}
console.log(it.next(13)) // => {value: 42, done: true}

可能結(jié)果跟你想象不一致傍药,接下來我們逐行代碼分析:

  • 首先 Generator 函數(shù)調(diào)用和普通函數(shù)不同,它會(huì)返回一個(gè)迭代器
  • 當(dāng)執(zhí)行第一次 next 時(shí)默穴,傳參會(huì)被忽略怔檩,并且函數(shù)暫停在 yield (x + 1) 處,所以返回 5 + 1 = 6
  • 當(dāng)執(zhí)行第二次 next 時(shí)蓄诽,傳入的參數(shù)12就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值,如果你不傳參媒吗,yield 永遠(yuǎn)返回 undefined仑氛。此時(shí) let y = 2 * 12,所以第二個(gè) yield 等于 2 * 12 / 3 = 8
  • 當(dāng)執(zhí)行第三次 next 時(shí)闸英,傳入的參數(shù)13就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值锯岖,所以 z = 13, x = 5, y = 24,相加等于 42

我們?cè)賮砜磦€(gè)例子:有三個(gè)本地文件甫何,分別1.txt,2.txt和3.txt出吹,內(nèi)容都只有一句話,下一個(gè)請(qǐng)求依賴上一個(gè)請(qǐng)求的結(jié)果辙喂,想通過Generator函數(shù)依次調(diào)用三個(gè)文件

//1.txt文件
2.txt

//2.txt文件
3.txt

//3.txt文件
結(jié)束

let fs = require('fs')
function read(file) {
  return new Promise(function(resolve, reject) {
    fs.readFile(file, 'utf8', function(err, data) {
      if (err) reject(err)
      resolve(data)
    })
  })
}
function* r() {
  let r1 = yield read('./1.txt')
  let r2 = yield read(r1)
  let r3 = yield read(r2)
  console.log(r1)
  console.log(r2)
  console.log(r3)
}
let it = r()
let { value, done } = it.next()
value.then(function(data) { // value是個(gè)promise
  console.log(data) //data=>2.txt
  let { value, done } = it.next(data)
  value.then(function(data) {
    console.log(data) //data=>3.txt
    let { value, done } = it.next(data)
    value.then(function(data) {
      console.log(data) //data=>結(jié)束
    })
  })
})
// 2.txt=>3.txt=>結(jié)束

從上例中我們看出手動(dòng)迭代Generator 函數(shù)很麻煩捶牢,實(shí)現(xiàn)邏輯有點(diǎn)繞,而實(shí)際開發(fā)一般會(huì)配合 co 庫去使用巍耗。co是一個(gè)為Node.js和瀏覽器打造的基于生成器的流程控制工具秋麸,借助于Promise,你可以使用更加優(yōu)雅的方式編寫非阻塞代碼炬太。

安裝co庫只需:npm install co

上面例子只需兩句話就可以輕松實(shí)現(xiàn)

function* r() {
  let r1 = yield read('./1.txt')
  let r2 = yield read(r1)
  let r3 = yield read(r2)
  console.log(r1)
  console.log(r2)
  console.log(r3)
}
let co = require('co')
co(r()).then(function(data) {
  console.log(data)
})
// 2.txt=>3.txt=>結(jié)束=>undefined

我們可以通過 Generator 函數(shù)解決回調(diào)地獄的問題灸蟆,可以把之前的回調(diào)地獄例子改寫為如下代碼:

function *fetch() {
    yield ajax(url, () => {})
    yield ajax(url1, () => {})
    yield ajax(url2, () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()

七、async/await

1.Async/Await簡介

使用async/await亲族,你可以輕松地達(dá)成之前使用生成器和co函數(shù)所做到的工作,它有如下特點(diǎn):

  • async/await是基于Promise實(shí)現(xiàn)的炒考,它不能用于普通的回調(diào)函數(shù)。
  • async/await與Promise一樣霎迫,是非阻塞的斋枢。
  • async/await使得異步代碼看起來像同步代碼,這正是它的魔力所在女气。

一個(gè)函數(shù)如果加上 async 杏慰,那么該函數(shù)就會(huì)返回一個(gè) Promise

async function async1() {
  return "1"
}
console.log(async1()) // -> Promise {<resolved>: "1"}

Generator函數(shù)依次調(diào)用三個(gè)文件那個(gè)例子用async/await寫法,只需幾句話便可實(shí)現(xiàn)

let fs = require('fs')
function read(file) {
  return new Promise(function(resolve, reject) {
    fs.readFile(file, 'utf8', function(err, data) {
      if (err) reject(err)
      resolve(data)
    })
  })
}
async function readResult(params) {
  try {
    let p1 = await read(params, 'utf8')//await后面跟的是一個(gè)Promise實(shí)例
    let p2 = await read(p1, 'utf8')
    let p3 = await read(p2, 'utf8')
    console.log('p1', p1)
    console.log('p2', p2)
    console.log('p3', p3)
    return p3
  } catch (error) {
    console.log(error)
  }
}
readResult('1.txt').then( // async函數(shù)返回的也是個(gè)promise
  data => {
    console.log(data)
  },
  err => console.log(err)
)
// p1 2.txt
// p2 3.txt
// p3 結(jié)束
// 結(jié)束

2.Async/Await并發(fā)請(qǐng)求

如果請(qǐng)求兩個(gè)文件,毫無關(guān)系缘滥,可以通過并發(fā)請(qǐng)求

let fs = require('fs')
function read(file) {
  return new Promise(function(resolve, reject) {
    fs.readFile(file, 'utf8', function(err, data) {
      if (err) reject(err)
      resolve(data)
    })
  })
}
function readAll() {
  read1()
  read2()//這個(gè)函數(shù)同步執(zhí)行
}
async function read1() {
  let r = await read('1.txt','utf8')
  console.log(r)
}
async function read2() {
  let r = await read('2.txt','utf8')
  console.log(r)
}
readAll() // 2.txt 3.txt

八轰胁、總結(jié)

1.JS 異步編程進(jìn)化史:callback -> promise -> generator -> async + await

2.async/await 函數(shù)的實(shí)現(xiàn),就是將 Generator 函數(shù)和自動(dòng)執(zhí)行器朝扼,包裝在一個(gè)函數(shù)里赃阀。

3.async/await可以說是異步終極解決方案了。

(1) async/await函數(shù)相對(duì)于Promise擎颖,優(yōu)勢(shì)體現(xiàn)在:

  • 處理 then 的調(diào)用鏈榛斯,能夠更清晰準(zhǔn)確的寫出代碼
  • 并且也能優(yōu)雅地解決回調(diào)地獄問題。

當(dāng)然async/await函數(shù)也存在一些缺點(diǎn)搂捧,因?yàn)?await 將異步代碼改造成了同步代碼驮俗,如果多個(gè)異步代碼沒有依賴性卻使用了 await 會(huì)導(dǎo)致性能上的降低,代碼沒有依賴性的話允跑,完全可以使用 Promise.all 的方式王凑。
(2) async/await函數(shù)對(duì) Generator 函數(shù)的改進(jìn),體現(xiàn)在以下三點(diǎn):

  • 內(nèi)置執(zhí)行器聋丝。

Generator 函數(shù)的執(zhí)行必須靠執(zhí)行器索烹,所以才有了 co 函數(shù)庫,而 async 函數(shù)自帶執(zhí)行器弱睦。也就是說百姓,async 函數(shù)的執(zhí)行,與普通函數(shù)一模一樣况木,只要一行垒拢。

  • 更廣的適用性。

co 函數(shù)庫約定焦读,yield 命令后面只能是 Thunk 函數(shù)或 Promise 對(duì)象子库,而 async 函數(shù)的 await 命令后面,可以跟 Promise 對(duì)象和原始類型的值(數(shù)值矗晃、字符串和布爾值仑嗅,但這時(shí)等同于同步操作)

  • 更好的語義张症。

async 和 await仓技,比起星號(hào)和 yield,語義更清楚了俗他。async 表示函數(shù)里有異步操作脖捻,await 表示緊跟在后面的表達(dá)式需要等待結(jié)果。

作者:浪里行舟
鏈接:JS 異步編程六種方案
來源:github
著作權(quán)歸作者所有兆衅。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)地沮,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處嗜浮。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市摩疑,隨后出現(xiàn)的幾起案子危融,更是在濱河造成了極大的恐慌,老刑警劉巖雷袋,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吉殃,死亡現(xiàn)場離奇詭異,居然都是意外死亡楷怒,警方通過查閱死者的電腦和手機(jī)蛋勺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸠删,“玉大人抱完,你說我怎么就攤上這事∪信荩” “怎么了乾蛤?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捅僵。 經(jīng)常有香客問我,道長眨层,這世上最難降的妖魔是什么庙楚? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮趴樱,結(jié)果婚禮上馒闷,老公的妹妹穿的比我還像新娘。我一直安慰自己叁征,他們只是感情好纳账,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捺疼,像睡著了一般疏虫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啤呼,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天卧秘,我揣著相機(jī)與錄音,去河邊找鬼官扣。 笑死翅敌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惕蹄。 我是一名探鬼主播蚯涮,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼治专,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了遭顶?” 一聲冷哼從身側(cè)響起张峰,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎液肌,沒想到半個(gè)月后挟炬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嗦哆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年谤祖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片老速。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡粥喜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橘券,到底是詐尸還是另有隱情额湘,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布旁舰,位于F島的核電站锋华,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏箭窜。R本人自食惡果不足惜毯焕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望磺樱。 院中可真熱鬧纳猫,春花似錦、人聲如沸竹捉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽块差。三九已至侵续,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憾儒,已是汗流浹背询兴。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留起趾,地道東北人诗舰。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像训裆,于是被迫代替她去往敵國和親眶根。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜀铲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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

  • 前言 我們知道Javascript語言的執(zhí)行環(huán)境是"單線程"。也就是指一次只能完成一件任務(wù)属百。如果有多個(gè)任務(wù)记劝,就必須...
    浪里行舟閱讀 14,314評(píng)論 1 27
  • 前言 Javcscript是單線程機(jī)制,單線程模型指的是族扰,JavaScript只在一個(gè)線程上運(yùn)行厌丑。也就是說,Jav...
    蛙哇閱讀 526評(píng)論 0 3
  • 眾所周知 JavaScript 是單線程工作渔呵,也就是只有一個(gè)腳本執(zhí)行完成后才能執(zhí)行下一個(gè)腳本怒竿,兩個(gè)腳本不能同時(shí)執(zhí)行...
    前端一菜鳥閱讀 569評(píng)論 0 7
  • 異步編程在JavaScript中非常重要。過多的異步編程也帶了回調(diào)嵌套的問題扩氢,本文會(huì)提供一些解決“回調(diào)地獄”的方法...
    AlienZHOU閱讀 27,490評(píng)論 2 52
  • 異步編程背景: JavaScript的執(zhí)行環(huán)境是“單線程”耕驰,即一次只能執(zhí)行一個(gè)任務(wù),如果有多個(gè)任務(wù)录豺,就需要排隊(duì)朦肘,前...
    BubbleM閱讀 238評(píng)論 0 1