es6非同步方法在專案中的實踐
author: teal.yao
polling
/// 輪詢
// promise version
function fetchProtocol_promise(token) {
let counter = 0;
return new Promise((resolve, reject) => {
polling();
function polling() {
fetch().then(() => {
if (data.status === 200) {
switch (data.result.state) {
case 2:
resolve(getPreviewURL(data.result.url))
break
case 1:
if (counter++ > 20) {
reject("合同建立超時,請重新再試")
}
// fetch again
setTimeout(polling, 300)
break
case 3:
reject("合同建立失敗")
break
}
}
})
}
});
}
/// async version
// 對比promise方式,會多了一個變數來管理狀態成功
// 利用了promise裡面的resolve接收的是一個promise,則會等待此promise的完成
async function fetch(token) {
let counter = 0;
return await polling();
async function polling() {
if (counter++ > 20) {
return [false, "合同建立超時,請重新再試"]
}
let data = await fetch()
if (data.status === 200) {
switch (data.result.state) {
case 2:
return [true, getPreviewURL(data.result.url)]
case 1:
// fetch again
return new Promise(resolve =>
setTimeout(function () {
// waiting for polling state change
resolve(polling());
}, 300)
);
case 3:
return [false, "合同建立失敗"]
}
}
}
}
複製程式碼
給promise設定timeout
/**
* timeout promise
*
* promise沒有原生的超時的方法,得借用其他方法去處理
*
* 1. promise的兩個最終狀態, fulfilled、 rejected
*
* 2.下面借用Promise.race來處理超時
*
* Promise.race(iterable),當iterable引數裡的任意一個子promise被成功或失敗後,
* 父promise馬上也會用子promise的成功返回值或失敗詳情作為引數呼叫父promise繫結的相應控制程式碼,
* 並返回該promise物件。
*/
/// https://italonascimento.github.io/applying-a-timeout-to-your-promises/
const http = require('http')
let get = function (url) {
return new Promise((resolve, reject) => {
// 假裝這裡不支援timeout引數
http.get({
url,
}, res => {
res.on('data', d => {
resolve(d.toString())
})
})
})
}
/// promise timeout + promise.race
function promiseTimeout(promise, ms) {
let timeout = new Promise((resolve, reject) => {
setTimeout(function () {
reject('timeout')
}, ms)
})
return Promise.race([promise, timeout])
}
promiseTimeout(get('http://github.com'), 5).then(text => {
console.log({
text
})
}).catch(err => {
console.log({
err
})
})
複製程式碼
回撥地獄的解決方案
/**
* ======解決回撥地獄的=========
*
* 常用的回撥套回撥,容易出現回撥地獄,造成程式碼的可讀性、維護性差
*
* 現藉助promise來解決
*/
class Form {
constructor(num, errMsg) {
this.num = num
this.errMsg = errMsg
}
validator(callback) {
callback(this.num < .4)
}
}
let form1 = new Form(.3, 'im .3')
let form2 = new Form(.7, 'im .7')
let form3 = new Form(.6, 'im .6')
// 1. 回撥地獄寫法
// 回撥之後還是回撥,無窮無盡
form1.validator(valid => {
if (valid) {
form2.validator(valid => {
if (valid) {
form3.validator(valid => {
if (valid) {
console.log({
valid
})
} else {
console.log({
err: form3.errMsg
})
}
})
} else {
console.log({
err: form2.errMsg
})
}
})
} else {
console.log({
err: form1.errMsg
})
}
})
// 2. use async/await
// 把callback包裝成promise,結合await同步寫法
// 提高程式碼的可讀性
let formSerialValidator = async function (forms) {
let valid = true
let errMsg = ''
// 用for來模擬表單的線性呼叫
for (let form of forms) {
// 利用await的同步寫法
// 接收promise的resolve傳值,或reject的傳值
// 保持 resolve/reject 傳值結構是一樣的: tuple [boolean, string]
[valid, errMsg] = await promiseFactory(form)
// .catch(errMsg => {
// // 保持與resolve返回結果一致
// return [false, errMsg]
// })
if (!valid) {
break
}
}
return [valid, errMsg]
// 將回撥包裝成promise
function promiseFactory(form) {
return new Promise((resolve, reject) => {
form.validator(valid => {
// if (valid) {
resolve([valid, form.errMsg])
// }
// reject(form.errMsg)
})
})
}
}
formSerialValidator([form1, form2, form3]).then(([valid, errMsg]) => {
console.log({
valid,
errMsg
})
})
// 3. promise + reduce
// 利用then的鏈式呼叫
// a().then(() => new Promise()).then(() => new Promise()) 第一個then為reject時,整個鏈的狀態就是reject
let reduceValidator = function (forms) {
// 用reduce生成校驗的Promise chain
// reduce的初始initValue得注意下,是Promise.resolve()
// Promise.resolve()傳遞的是一個具體的值(undefined),所以狀態為fulfilled,可直接使用then呼叫
// Promise then方法返回的promise,則等待該promise的返回
return forms.reduce((promise, form) => {
return promise.then($ => {
return new Promise((resolve, reject) => {
form.validator(valid => {
console.log(valid)
// resolve 只能有一個引數
if (valid) {
resolve(true)
}
reject(form.errMsg)
})
})
})
}, Promise.resolve())
}
reduceValidator([form1, form2, form3]).then(valid => {
console.log({
valid,
})
}).catch(errMsg => {
console.log({
errMsg
})
})
// Promise then方法返回的promise,則等待該promise的返回
Promise.resolve(new Promise((resolve, reject) => {
resolve(1)
})).then(res => {
console.log(res)
})
複製程式碼
表單驗證的yield寫法
/// 傳統表單的校驗
// 一般人的寫法
// this.$message.error重複呼叫,不利於維護
function validator1() {
let msg = ''
if (fd.displayPosition === null) {
this.$message.error('請選擇傳送端')
// return false
} else if (fd.type === null) {
this.$message.error('請選擇通知方式')
// return false
} else if (fd.peroid === undefined || JSON.stringify(fd.peroid) === '[null,null]') {
this.$message.error('請選擇有效日期')
// return false
} else if (self.isShowNum && fd.showNum === null) {
msg = '請選擇彈出次數'
// return false
}
this.$message.error(msg)
return true
}
// 二般人的寫法
// 利用yield給暫停一下
// generator函式呼叫後生成一個Iterator物件,返回的done為true時結束
// Iterator: {
// next() {
// return {
// value: any,
// done: boolean
// }
// }
// }
//
// for..of 實現了Iterator介面,可以呼叫generator函式的生成物件
function validator2() {
function* check() {
if (fd.displayPosition === null) {
yield '請選擇傳送端'
}
if (fd.type === null) {
yield '請選擇通知方式'
}
if (fd.peroid === undefined || JSON.stringify(fd.peroid) === '[null,null]') {
yield '請選擇有效日期'
}
if (self.isShowNum && fd.showNum === null) {
yield '請選擇彈出次數'
}
}
let iterator = check()
for (let msg of iterator) {
// if (msg !== undefined) {
this.$message.error(msg)
return false
// }
}
return true
}
複製程式碼
promise/async的錯誤處理
/// 1. promise內部的錯誤,不會中斷外部的執行
new Promise((resolve, reject) => {
throw Error('promise')
console.log(1)
})
console.log('after promise error')
// 可使用try-catch或Promise.prototype.catch處理
new Promise((resolve, reject) => {
try {
throw Error('promise')
} catch (err) {
console.log(err)
}
})
console.log('after promise error')
// 與上面的捕獲錯誤的方法對比,throw Error後面的程式碼將無法執行
new Promise((resolve, reject) => {
throw Error('promise')
console.log('after inner error')
})
.catch(err => {
console.log(err)
// return 'form catch'
throw Error('from catch')
})
.catch(res => {
console.log(res)
})
console.log('after promise error')
/// 2. async函式的錯誤處理
// 處理await返回的錯誤,promise不捕獲,則留給async函式處理
// promise前面加await與不加的區別
// 觀察下面3種情況:
// 1. 內部promise錯誤不處理
// 2. 內部promise加上catch
// 3. 內部promise接上await
async function usePromiseCatch() {
new Promise((resolve, reject) => {
throw Error('throw')
console.log(330)
})
// .catch(err => {
// console.log(err)
// })
console.log('continue')
}
usePromiseCatch().catch(err => {
console.log('in async catch', err)
})
複製程式碼