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')
})