本文主要介紹了我的對promise的一些粗淺理解,若有不正確的地方,還請指正。css
從JS語法層面上講,Promise是一個函數,能夠經過new關鍵字進行調用而後生成一個對象,這個對象被成爲promise對象。promise對象有三種狀態,爲pedding、resolved、rejected,這是promise的重要機制。 從功能的角度來說,Promise是一個容器,它裏面包含着一個將來纔會獲得的(一般是異步)結果。 Promise幫助調用者拿回了調用回調函數的主動權,而不是把主動權交給第三方函數。前端
如下關於callback函數的執行有幾種信任問題。vue
function getData(url, callback) {
let req = new XMLHttpRequest()
req.open('GET', url, true)
req.onload = function () {
if (req.status === 200) {
// 使用回調處理會存在幾種潛在的問題
// 1. 過早或者過晚調用
// 2. 乾脆不調用
// 3. 調用了不少次
// 4. 沒有給回調函數傳入指定的參數
callback(req.responseText)
} else {
console.log(new Error(req.statusText))
}
}
req.onerror = function () {
console.log(new Error(req.statusText))
}
req.send()
}
function callback(text) {
console.log(text)
}
let url = 'http://api.myjson.com/bins/16hvos'
getData(url, callback)
複製代碼
function getData(url) {
return new Promise(function (resolve, reject) {
let req = new XMLHttpRequest()
req.open('GET', url, true)
req.onload = function () {
if (req.status === 200) {
resolve(req.responseText)
} else {
reject(new Error(req.statusText))
}
}
req.onerror = function () {
reject(new Error(req.statusText))
}
req.send()
})
}
let url = 'http://api.myjson.com/bins/16hvos'
getData(url).then(function onFullfilled(value) {
console.log(value)
}).catch(function onRejected(value) {
console.log(value)
})
複製代碼
回調地獄會致使代碼晦澀難懂,不易維護,錯誤處理複雜。ios
// 先執行taskA 獲得符合預期結果; 執行taskB, 獲得預期結果,執行taskC, 獲得預期結果,接着處理。。。
function taskA(callback) {
let result = 1
setTimeout(() => {
callback(result)
}, 2000)
}
function taskB(callback) {
let result = 2
setTimeout(() => {
callback(result)
}, 2000)
}
function taskC(callback) {
let result = 3
setTimeout(() => {
callback(result)
}, 2000)
}
taskA(function (res) {
if (res === 1) {
taskB(function (res) {
if (res === 2) {
taskC(function (res) {
if (res === 3) {
console.log(res)
}
})
}
})
}
})
複製代碼
能夠看出每執行一個函數都須要給其一個回調函數,並且代碼末尾有不少括號的嵌套,雖然可使用必定方法能夠改善這種嵌套的方法,可是依然代碼依然不夠易讀。好比採用以下方法抽離一部分代碼git
function a(res) {
if (res === 1) {
taskB(b)
}
}
function b (res) {
if (res === 2) {
taskC(c)
}
}
function c(res) {
if (res === 3) {
console.log(res)
}
}
taskA(a) // 3
複製代碼
即便採用以上方式處理,閱讀此代碼仍然讓人頭大。es6
// taskA執行,拿到預期結果,返回一個resolved的promise對象
function taskA() {
let result = 1
return new Promise(function (resolve, reject) {
setTimeout(() => {
if (result === 1) {
resolve(result)
} else {
reject(new Error('fail'))
}
}, 2000)
})
}
// 一個resolved狀態的promise對象能夠在其then方法中拿到異步處理的結果
// 在then方法中的回調函數中使用return會新生成一個resolved狀態的promise對象
taskA().then(function (result) {
return result
}).then(function taskB() {
let result = 2
return new Promise(resolve => {
setTimeout(() => {
resolve(result)
}, 2000)
})
}).then(function taskC() {
let result = 3
return new Promise(resolve => {
setTimeout(() => {
resolve(result)
}, 2000)
})
}).then(function (result) {
console.log(result)
})
複製代碼
Promise函數接受一個函數做爲參數,這個函數有兩個參數,resolve和reject, 用來改變所生成的promise對象的狀態。這個函數參數會當即執行。github
let promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve(42)
}, 2000)
})
promise.then(function (value) {
console.log(value) // 42
})
Object.getPrototypeOf(promise) === Promise.prototype // true
複製代碼
Promise.resolve() 直接返回一個已解析狀態的promise對象。vuex
var promise = Promise.resolve(42)
// 至關於
var promise = new Promise(function (resolve, reject) {
resolve(42)
})
複製代碼
Promise.reject 直接返回一個已拒絕狀態的promise對象。json
var promise = Promise.reject(new Error('ok'))
// 至關於
var promise = new Promise(function (resolve, reject) {
reject(new Error('fail'))
})
複製代碼
(1) 每個promise對象都有then方法,當這個promise對象的狀態變爲resolved時,會執行then中註冊的回調函數。axios
let promise = Promise.resolve(42)
promise.then(function (value) {
console.log(value) //42
})
複製代碼
(2) promise對象的then方法會返回一個新的promise對象,所以咱們能夠在新生成的promise對象中繼續使用then方法。
let promise = Promise.resolve(42)
promise.then(function (value) {
console.log(value)
}).then(function (value) {
console.log('ok')
})
複製代碼
根據then方法的特性能夠實現promise對象的鏈式調用。
let promise = Promise.resolve()
function taskA() {
console.log('taskA')
}
function taskB() {
console.log('taskB')
}
function onRejected(error) {
console.log(error)
}
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
// taskA taskB
複製代碼
以上例子中,實現了promise的鏈式調用。當taskA和taskB中沒有拋出錯誤時,程序會一直執行下去,其中catch是爲了捕獲taskA或taskB出現的異常。若是taskA或者taskB中出現了錯誤,那麼catch中的回調函數就會執行。以下:
let promise = Promise.resolve()
function taskA() {
throw new Error('fail')
}
function taskB() {
console.log('taskB')
}
function onRejected(error) {
console.log(error)
}
promise
.then(taskA)
.then(taskB)
.catch(onRejected)
// Error fail
複製代碼
若是taskA出現了一個錯誤,那麼taskB將不會執行,會執行調用catch中的回調函數。 3). 鏈式調用傳值 想要實現鏈式調用的傳值,只須要在then中的回調函數給出返回值,Promise就會自動將這個返回值解析爲一個promise對象。
let promise = Promise.resolve(10)
function taskA(value) {
return value + 10
}
function taskB(value) {
return value + 10
}
function taskC(value) {
console.log(value)
}
promise
.then(taskA)
.then(taskB)
.then(taskC) // 30
複製代碼
當一個promise的狀態爲rejected時,那麼它的catch方法中的回調函數將會被執行。catch方法其實就是then方法中第二個回調函數。
let promise = Promise.reject('fail')
promise.catch(function (error) {
console.log(error) // fail
})
// 至關於
promise.then(undefined, function (error) {
console.log(error) // fail
})
複製代碼
catch也會返回一個promise對象。
Promise.all接受一個由promise對象組成的數組參數,當每一個promise的狀態都變成resoved或rejected時,Promise.all返回的promise對象的狀態纔會改變(resolved或者rejected)。
// getData函數返回一個promise, 而且函數體內的Promise函數會當即執行,即當即執行函數體內的異步任務
function getData(url) {
return new Promise(function (resolve, reject) {
var req = new XMLHttpRequest()
req.open('GET', url, true)
req.onload = function () {
if (req.status === 200) {
return req.responseText
} else {
return new Error(req.statusText)
}
}
req.onerror = function () {
reject(new Error(req.statusText))
}
req.send()
})
}
// 錯誤寫法
// getData函數前加了async關鍵字,所以它也返回一個promise對象,可是函數體內的請求時異步的,
// getData會當即執行,而後再去執行異步任務,所以getData返回的promise永遠是resolved undefined
async function getData(url) {
var req = new XMLHttpRequest()
req.send()
req.open('GET', url, true)
req.onload = function () {
if (req.status === 200) {
console.log('ok')
return req.responseText
} else {
return new Error(req.statusText)
}
}
req.onerror = function () {
reject(new Error(req.statusText))
}
}
Promise.all([
getData('http://azu.github.io/promises-book/json/comment.json'),
getData('http://azu.github.io/promises-book/json/people.json')
]).then(res => {
console.log(res)
})
複製代碼
Promise.race也接受一個由promise對象組成的數組參數,當其中的一個promise對象的狀態變爲resolved或是rejected時,Promise.race返回的promise對象纔會改變(resolved或者rejected)。
function asyncPromise () {
return new Promise(resolve => {
resolve(3)
})
}
function test() {
asyncPromise().then(function (value) {
console.log(value)
})
console.log(1)
}
test()
console.log(2)
// 輸出順序爲 1 2 3
複製代碼
能夠把async當作是一個返回值爲promise對象的函數的語法糖。
async function test() {
return 1
}
test() // Promise {<resolved>: 1} google瀏覽器的打印結果
// 至關於
function test() {
return Promise.resolve(1)
}
test() // Promise {<resolved>: 1} google瀏覽器的打印結果
複製代碼
await 讓異步代碼變爲了同步(同步執行)。
function asyncFunction () {
return new Promise(resolve => {
resolve(2)
})
}
async function test() {
let value = await asyncFunction()
console.log(value)
console.log(3)
}
test()
console.log(1)
// 打印結果爲 1 2 3
複製代碼
採用兩種方式模擬一個通過vuex獲取數據的過程。 (1) 直接返回promise對象的方式
// src/People.vue
created () {
this.$store.dispatch('GetPeople', { page: 1, size: 20 }).then(response => {
console.log(response) // [{ id: 1, name: 'Joe'}]
})
}
// src/store/people/actions.js
GetPeople({ commit }, payload) {
return new Promise(function (resolve, reject) {
axios.get('http://www.example.com/people', { payload })
.then(function (response) {
resolve(response) // response = [{ id: 1, name: 'Joe'}]
}).catch(function (error) {
reject(error)
})
})
}
複製代碼
(2) async / await
// src/People.vue
async created() {
await this.$store.dispatch('GetPeople', { page: 1, size: 20 })
// [{ id: 1, name: 'Joe'}]
}
// src/store/people/actions.js
async GetPeople({ commit }, payload) {
const response = await axios.get('http://www.example.com/people', { payload })
return response
}
複製代碼
參考:
咱們是來自帝都的一枚前端程序猿 + 一枚前端程序媛。
這裏發佈的文章是咱們對學習內容的總結,預計會每週至少會更新一篇。
目前咱們學習計劃是: 小程序實戰 => vue 進階用法 => vue 原理 => css 基礎 => es6 => js 深刻
另外,工做中用到的一些技術和完成的功能,咱們也會及時總結更新在這裏
如文章有錯誤或表述不清晰,歡迎各位留言反饋~~