promise
promise 可以說是很常用的非同步處理方法
比如我們使用promise封裝一個canvas截圖的方法
clip (resolve, reject) {
// 截圖 獲取圖片url
return new Promise((resolve, reject) => {
html2canvas(document.getElementById('view'), {
canvas: canvas,
onrendered: (canvas) => {
let getImg = canvas.toDataURL('image/png')
resolve(getImg)
}
})
})
}
複製程式碼
實現一個簡單的promise
function Promise (executor) {
let self = this
self.status = 'pending'
self.success // 成功的原因
self.failure // 失敗的原因
// 事件池
self.resolvePool = []
self.rejectPool = []
function resolve (data) {
if (self.status == 'rejected') return
self.status = 'resolved'
self.success = data
// 執行佇列
self.resolvePool.forEach(function (cb){
cb(self.success)
})
}
function reject (data) {
if (self.status == 'resolved') return
self.status = 'rejected'
self.failure = data
// 執行佇列
self.rejectPool.forEach(function (cb){
cb(self.failure)
})
}
// new一個例項就會立即執行這個promise
executor(resolve, reject)
}
// then
Promise.prototype.then = function (onFulfilled, onRejected) {
let self = this
let promiseStatus = self.status
if (promiseStatus == 'resolved') {
onFulfilled(self.success)
}
if (promiseStatus == 'rejected') {
onRejected(self.failure)
}
// 很有可能此時狀態為 pending
if (promiseStatus == 'pending') {
// 放入事件池中等待執行
self.resolvePool.push(function () {
onFulfilled(self.success)
})
self.rejectPool.push(function () {
onRejected(self.failure)
})
}
}
複製程式碼
Iterator
let target = beIteraor({name: 'mxx', address: 'beijing'})
target.next()
target.next()
複製程式碼
Iterator 是一個迭代器物件 每次從集合中取出一項 並且跟蹤當前序列所在位置
通過使用next方法 返回一個包含value和done兩個屬性的物件
{value: 當前物件成員, done: Boolean}
複製程式碼
Iterator 簡易實現
let target = beIteraor(['mxx', 'beijing'])
let a = target.next()
let b = target.next()
let c = target.next()
// { value: 'mxx', done: false } { value: 'beijing', done: false } { value: 'undefined', done: true }
// 返回一個具有next方法的物件
function beIteraor(ary) {
let index = 0
let len = ary.length
return {
next: function () {
// 呼叫next方法 返回 {value, done} 並且指標移動位置
let done = ~~index == ~~len
// 如果指標位置移動到末尾 則返回undefined 否則返回當前位置成員
let value = done ? 'undefined' : ary[index]
index++
return {value, done}
}
}
}
複製程式碼
generator
- generator 函式生成一個迭代器
<!-- 函式 注: 這裡的read函式直接返回檔案中內容 -->
function * getCont () {
let name = yield 'youchangjing'
let address = yield 'beijing'
return name + address
}
<!-- 如何執行 -->
let getC = getCont()
getC.next() // {value: 'youchangjing', done: false}
getC.next() // {value: 'beijing' , done: false}
getC.next() // {value: undefined , done: true}
複製程式碼
可以看到 generator 函式呼叫和普通函式一樣 fn() 即可 但是函式並不會執行 只有當呼叫next方法 才能執行到第一個狀態
generator 是分段執行的 yield表示暫停執行 next方法恢復函式執行
目前來說 瀏覽器對 generator 支援情況還是很不錯的
co
如果 yield 後面是一個 promise 函式 可以配合co 庫來使用
<!-- read函式 -- 封裝的一個簡易的promise -->
function read(dir) {
return new Promise((resolve, reject) => {
fs.readFile(dir, 'utf-8', (err, cont) => {
if (err) reject(err)
resolve(cont)
})
})
}
<!-- generator -->
function * getCont () {
let name = yield read('name.js')
let address = yield read('address.js')
return name + address
}
<!-- 配合co庫 -->
co(getCont()).then(function (cont) {
console.log(cont)
})
複製程式碼
簡單的CO實現原理
-
co 的引數是一個迭代器
-
co 返回的是promise 返回的promise 接受 generator 函式的 value
-
co 內部可以使 generator 函式 一直執行到 done 為TRUE
function co(iterator) {
return new Promise(function (resolve, reject) {
function next(cont) {
let {value, done} = iterator.next(cont)
<!-- value 也是一個promise -->
if (done) {
<!-- 如果 done 為TRUE 則將value 傳入 resolve -->
resolve(value)
} else {
<!-- done 為FALSE 則執行其then 方法 用於獲取其data傳遞給 next -->
value.then(function (data) {
next(data)
<!-- next 遞迴 -->
}, reject)
}
}
next()
})
}
複製程式碼
async await
async await 可以看做 co + generator 的語法糖
雖然co庫可以幫我們自行處理generator 但是又要使用yield 又要封裝promise 也是有點麻煩 所以轉向 ES7 中的 async await
目前 async await 在Bable, Node 7+ 中被支援
async function getCont() {
let people = await read('./file.js')
let who = await read('./who.js')
return people + who
}
getCont().then((data) => console.log(data))
複製程式碼
- async 返回 promise
await
await 顧名思義 等待。那他在等待什麼呢。 這個取決於await 後面跟著的內容
await 'mxx'
<!-- 等待非promise -->
await IamPromise()
<!-- 等待 promise -->
複製程式碼
如果 await 等待的不是一個 promise 那麼await表示式的運算結果就是 它等到的東西 (其實await會將其轉為一個立即resolve的promise物件)
如果 await 等待的是promise 那麼他會阻塞後面的程式碼 等著Promise 物件 resolve 然後得到其值作為 await表示式的運算結果
function getRank () {
return 12345
}
async function getType () {
let name = await read('who.js')
<!-- read為promise -->
console.log(name)
let rank = await getRank()
<!-- getRank 為普通函式 -->
console.log(rank)
}
複製程式碼
- 如果我們在普通函式中使用await會被阻塞嗎
好吧~~ 直接報錯了
錯誤捕獲
async 中有兩種錯誤處理方式
1 可以在 async 函式中 使用try catch
async 中對try catch方法做了處理 使其可以捕獲非同步的錯誤
async function () {
try {
} catch (e) {
console.log(e)
}
}
複製程式碼
2 在then中進行錯誤捕獲
getCont().then().catch((e) => console.log(e))
複製程式碼
如果async函式中使用了try catch 那麼後面的then方法將會進入成功態 【相當於promise返回的是 undefined 】