異步編程
js中的單線程是指負責執行代碼的線程只有一個,這樣設計的初衷是js是用來操作DOM的,多線程會產生DOM操作衝突。
缺點:執行耗時任務時會阻塞代碼執行,出現假死狀態。
解決:有同步模式和異步模式。JS代碼執行是單線程,但是瀏覽器是多線程的,因此可以借用瀏覽器的多線程去執行一些耗時任務,如ajax請求。是同步模式還是異步模式是由運行環境提供的API來決定的。
取個例子:一段代碼中用promise封裝了一個ajax請求,利用瀏覽器的多線程去請求這個ajax請求,該段代碼繼續執行,請求完成後,promise.then中的回調函數會添加到代碼段的最後去執行。這樣,解決了耗時任務阻塞代碼執行的問題。
回調函數
回調函數就是將你想要做的事情寫在一個函數中,並把這個函數作爲另一個函數的參數交給執行者去執行。舉個例子:
// 回調函數作爲參數交給執行者,讓執行者決定想什麼情況下執行
function foo(callback) {
setTimeout(function() {
callback()
}, 1000)
}
// 回調函數
function callback() {
console.log('這是我想要做的事情')
}
foo(callback)
Promise
promise初始化
Promise是一個構造函數,promise對象初始化如下:
// 參數是一個函數,函數裏面是你需要執行的函數,並根據結果調用相應的函數,
// resolve將狀態改爲resolved,並將結果傳給then中的第一個回調函數參數
// resolve將狀態改爲rejected,並將結果傳給then中的第二個回調函數參數
const p = new Promise(function(resolve, reject) {
resolve('success')
})
// 不同結果的相應回調函數,成功了執行第一個函數,失敗了執行第二個函數
p.then(res => {
console.log(res)
}, err => {
console.log(err)
})
用Promise封裝Ajax請求:
function ajax (url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.onload = function () {
if(this.status === 200){
resolve(this.response)
}else{
reject(new Error(this.statusText))
}
}
xhr.responseType = 'json'
xhr.open('GET', url)
xhr.send()
})
}
ajax('https://www.ctwing.cn/sysCategory/getCatTreeByCode/homepage_nav').then(res => {
console.log('success', res)
}, err => {
console.log('fail', err)
})
鏈式調用
如果一個請求的發起需要在另一個請求成功的基礎上,那麼如果寫在前一個then回調函數中,同樣會造成地獄回調的問題。promise提供了鏈式調用來解決這個問題。
promise.then返回的是一個全新的promise對象(不是像以往的那種return this來實現的)。鏈式調用時,每一個then方法都在爲上一個then返回的promise對象添加回調。
promise.then中也可以手動return一個promise對象作爲下一個then方法的promise對象。
異常處理
在鏈式調用中,如果異常捕獲寫在.then的第二個參數中,則只能捕獲當前promise的異常。如果寫在鏈條的最末端,那麼可以捕獲到鏈條上的所有異常,鏈條上任何一個異常都會被傳遞,直至被捕獲。
靜態方法
Promise.resolve() 快速創建一個狀態爲resolved的promise對象
- 參數是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的 Promise 對象
- 參數是一個 Promise 實例,那麼Promise.resolve將不做任何修改、原封不動地返回這個實例。
- 參數是一個有then方法的對象,將這個對象轉爲 Promise 對象,然後就立即執行thenable對象的then方法
- 參數爲空,直接返回一個resolved狀態的 Promise 對象,resolvedValue爲undefined
Promise.reject() 快速創建一個狀態爲reject的promise對象
const p = new Promise((resolve, reject) => {
resolve('a promise Object')
})
const p1 = Promise.resolve('foo')
// Promise {<resolved>: "foo"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "foo"
const p2 = Promise.resolve(p)
console.log(p2 === p)
// true
const p3 = Promise.resolve({
name: 'tom',
then: function (resolve, reject) {
console.log('name is: ' + this.name)
resolve(this.name)
}
})
// name is: tom 立即執行then方法
// Promise {<resolved>: "tom"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "tom"
const p4 = Promise.resolve()
// Promise {<resolved>: undefined}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: undefined
Promise.all() 參數爲promise對象數組。返回一個新的promise對象,等待所有結果成功,新對象才返回成功,並將所有的結果傳遞給新對象,如果其中一個失敗,則新對象失敗,並返回第一個失敗的結果
Promise.race() 返回第一個完成的任務的結果
const pro1 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success1')
}, 1000)
})
const pro2 = new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success2')
}, 3000)
})
const pro3 = new Promise((resolve, reject) => {
setTimeout(function () {
reject('failed1')
}, 2000)
})
const proAll = Promise.all([pro1, pro2])
// Promise {<resolved>: Array(2)}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: Array(2)
// 0: "success1"
// 1: "success2"
// length: 2
// __proto__: Array(0)
const proRace = Promise.race([pro1, pro2, pro3])
// Promise {<resolved>: "success1"}
// __proto__: Promise
// [[PromiseStatus]]: "resolved"
// [[PromiseValue]]: "success1"
小練習:帶有timeout的請求結果
function request () {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve('success')
}, 2000)
})
}
function timeout () {
return new Promise((resolve, reject) => {
setTimeout(function () {
reject('time out!')
}, 500)
})
}
const newRequst = Promise.race([request(), timeout()])
newRequst.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})