這裏不講仿寫,主要講本身的所得;最近又回顧了一下promise
的實現;以及async-await
,promise 串行,限流並行;妙哉妙哉。vue
promise
的關鍵點在於進度條(狀態機)的控制最值得改進本身曾經代碼的就是它;不少時候咱們請求完接口,接着又要請求下一個,可能就嵌套着寫了,ios
ajax(12).then(res =>
ajax(res).then(res1 =>
ajax(res1).then(res2 =>
ajax(res2).then(val => {
console.log(val);
})
)
)
);
複製代碼
遠觀代碼,咦,整齊劃一,其實不該該這樣寫的,曾經的你中招了嘛~~~ 悄悄的去改改吧ajax
ajax(1)
.then(res => ajax(res))
.then(res1 => ajax(res1))
.then(res2 => ajax(res2))
.then(val => {
console.log(val);
})
.catch(err => err); //再來一個兜底
複製代碼
近觀慢動做,也是整齊劃一,妙哉妙哉axios
上面的代碼例子就是一個串行請求,能夠寫一個通用的函數數組
//values: () => Promise<any>[]
function promiseReduce(values) {
return values.reduce(
(p, n) =>
(p = p.then(
() => n(),
err => err
)),
Promise.resolve() //默認值加上
);
}
複製代碼
new Promise(res => {
let result = [];
ajax(1)
.then(res => {
result.push(res);
return ajax(2);
})
.then(res1 => {
result.push(res1);
return ajax(3);
})
.then(res2 => {
result.push(res2);
return ajax(4);
})
.then(res3 => {
result.push(res3);
res(result);
})
.catch(err => err);
}).then(data => {
console.log(data);
});
複製代碼
let result = [];
ajax(1)
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(2))
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(3))
.then(val => Promise.resolve(result.push(val)))
.then(() => ajax(4))
.then(val => Promise.resolve(result.push(val)))
.then(() => {
console.log(result, 'f');
})
.catch(err => err);
複製代碼
改進上面代碼promise
list = [() => ajax(), () => ajax(), ...]
,那就將其改爲[() => ajax(), Promise.resolve(result.push(val))), () => ajax(), Promise.resolve(result.push(val))),...]
promiseReduce
function g(val) {
val && result.push(val);
}
function promiseReduce(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)).then(res => n(res)),
err => err
));
}, Promise.resolve());
}
//也能夠這樣寫
function promiseReduce1(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)),
err => err
)).then(() => n());
}, Promise.resolve());
}
複製代碼
對於 reject 的狀況,能夠每一個 promise 兜底,也能夠最後兜底。可是結果不同 調試代碼以下bash
function ajax(val) {
return new Promise((res, rej) => {
setTimeout(() => {
res(val);
}, 1000);
});
}
function g(val) {
val && result.push(val);
}
function promiseReduce(values, fn) {
return values.reduce((p, n) => {
return (p = p.then(
val => Promise.resolve((fn(val), val)).then(res => n(res)),
err => err
));
}, Promise.resolve());
}
function reject(val) {
return new Promise((res, rej) => {
setTimeout(() => {
rej('error');
}, 1000);
});
}
//最後兜底
// function promiseReduce(values, fn) {
// return values.reduce((p, n) => {
// return (p = p.then(val => Promise.resolve((fn(val), val)))).then(() => n());
// }, Promise.resolve());
// }
promiseReduce(
[() => reject(), () => ajax(1), () => ajax(2), () => ajax(3), , () => ajax(4), () => ajax(5)],
g
)
.then(val => {
result.push(val);
console.log(result);
})
.catch(err => {
console.log(err);
});
複製代碼
promise.all
,也能夠改爲串行//每隔兩秒去請求
function ajax(val) {
return new Promise((resovle, reject) => {
setTimeout(() => {
resovle(val);
console.log(val, 'val');
}, val * 1000);
}).catch(err => err);
}
let l = [ajax, ajax, ajax, ajax, ajax];
let config = 2;
let p = Promise.resolve(config);
function fn(p) {
return function (e, i) {
return (p = p.then(res => e(res))); //這邊玩法挺多的😄,有待玩耍
};
}
l = l.map(fn(p));
Promise.all(l).then(res => {
console.log(res);
});
複製代碼
promise.all
能夠用來實現限流 ,能夠看這篇文章,頗有意思我羅列了幾個關鍵點,歡迎食用😋併發
promise
,後面都加入回調隊列中build (fn) {
if (this.count < this.limit) {
return this.run(fn)
} else {
return this.enqueue(fn)
}
}
複製代碼
async run (fn) {
this.count++
// 維護一個計數器
const value = await fn()
this.count--
// 執行完,看看隊列有東西沒
this.dequeue()
return value
}
複製代碼
下面這段代碼能作到,app
其一:利用達到限流後加入的promise
是新的promise
,放入promise.all
中,異步
其二:新的promise
它的狀態改變後返回的結果,也就是最後promise.all()
返回到數組中的結果元素
其三: 新的promise
將狀態控制權放入回調隊列中,等待去執行this.run(fn).then(resolve).catch(reject)
enqueue (fn) {
//新搞一個promise
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject })
})
}
dequeue () {
if (this.count < this.limit && this.queue.length) {
const { fn, resolve, reject } = this.queue.shift()
//await fn() 執行完將結果帶入resolve(value)並結束其狀態
this.run(fn).then(resolve).catch(reject)
}
}
複製代碼
核心我認爲是:promise
的進度條怎麼控制或者怎麼轉移,你們都知道是經過resolve
或者reject
。怎麼控制,怎麼玩它就是很大的學問了;每一個人的想法思路不一樣,各類組合實現就成了萬花筒,妙哉妙哉
由於一個promise
的自身的狀態是惟一的,由pending
開始到resolved
或者rejected
(這邊用這兩個名詞代替); then
中的返回值是一個接力棒(並始終返回一個新的promise
);又是一個新的開始。進度條控制也就轉交給了它。 axios 中間件,async-await 仿寫,限流等都有用到
頁面上有一個輸入框,兩個按鈕,A 按鈕和 B 按鈕,點擊 A 或者 B 分別會發送一個異步請求,請求完成後,結果會顯示在輸入框中。
我以前第一想法是 維護一份回調隊列,每次點擊往隊列中加入回調。開關的控制在第一次這個思路相似 vue 的合併更新策略。其實這個思路是錯的。理由以下: 主要緣由就是:
js去執行a.click(),b.click()
和用戶點擊按鈕a,按鈕b
實際上是不同的,a.click()
,其中有微任務的加到微任務隊列中,有宏任務的加入宏任務中;再去執行b.click()
同理;主線程完事了,再去微任務,而後宏任務;a.click()
的全部任務,而後再去執行b.click()
2.正確解法
const p = Promise.resolve();
function AClick() {
p = p.then(() => {
return new Promise((resolve, reject) => {
ajax().then(() => {
resolve();
console.log('A');
});
});
});
}
function BClick() {
p = p.then(() => {
return new Promise((resolve, reject) => {
ajax().then(() => {
resolve();
console.log('B');
});
});
});
}
複製代碼
若是按鈕不少能夠寫個函數對每一個按鈕包裝一層,相似裝飾器