一学期的比赛生涯在 “” 中结束了, 无论如何; 开始了新学期的学习.那么这学期主攻学业, 但是前端还是要在子任务进度下慢慢推进, 本文算是前端继续学习的一个开端~
面试面什么?

- 熟练使用Promise封装代码,如封装axios,Promise级联使用,掌握async、await的用法
- 掌握Promise静态方法的使用场景、用法,Promise.all、Promise.allSettled、Promise.race、Promise.any、Promise.resolve、Promise.reject
- 手写Promise.all、Promise.race、Promise.allSettled
- 手写Promise.any、Promise.last、Promise.queue
- 并发请求限制数量封装
- 实现Promisify
- Promise 微任务输出、async/await 微任务输出
- 理解Promise A+规范,手写Promise
手写 Promise.all 原理
Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Promise.all = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) if(promises.length === 0) return resolve([]) let values = [] let count = 0 for(let i=0; i < promises.length; i++) { promises[i].then(v => { values[i] = v count++ if(count === promises.length) { resolve(values) } }, reject) } }) }
|
手写 Promise.race 原理
Promise.race(iterable)方法返回一个promise, 一旦迭代器中的某个 promise resolve 或者 reject, 返回的 promise 就会 resolve 或reject.
Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Promise.race = function(iterable) { let arr = [...iterable].map(item => item instanceof Promise? item : Promise.resolve(item))
return new Promise((resolve, reject) => { for(let i=0; i<arr.length; i++) { arr[i].then(resolve, reject) } }) }
let p1 = new Promise(r => setTimeout(r, 3000, 1)) let p2 = new Promise((r,j) => setTimeout(j, 1000, 2)) let p3 = new Promise(r => setTimeout(r, 500, 3))
Promise.race([p1, p2, p3]) .then(data => console.log(data)) .catch(e => console.error(e))
Promise.race('hello').then(data => console.log(data))
console.log(Promise.race(''))
Promise.race([Promise.resolve(2), 3]).then(data => console.log(data))
|
手写 Promise.allSettled 原理
Promise.allSettled() 方法返回一个在所有给定的promise都已 fuldilled 或 rejected 后的 promise, 并带有一个对象数组, 每个对象表示对应的 promise 结果.
Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| Promise.allSettled = function(iterable) { let arr = [...iterable].map(item => item instanceof Promise ? item : Promise.resolve(item)) if(arr.length === 0) return Promise.resolve([])
return new Promise((resolve, reject) => { let results = [] let count = 0 for(let i in arr) { arr[i].then(value => { results[i] = { status: 'fulfilled', value } }, reason => { results[i] = { status: 'rejected', reason } }).finally(() => { count++ if(count === arr.length) { resolve(results) } }) } }) }
let p1 = new Promise(r => setTimeout(r, 3000, 1)) let p2 = new Promise((r,j) => setTimeout(j, 1000, 2)) let p3 = new Promise(r => setTimeout(r, 500, 3))
Promise.allSettled([p1, p2, p3]) .then(data => console.log(data)) .catch(e => console.error(e))
Promise.allSettled('hello').then(data => console.log(data))
Promise.allSettled('').then(data => console.log(data))
Promise.allSettled([Promise.resolve(2), 3, Promise.reject(4)]).then(data => console.log(data))
|
手写 Promise.any 原理
Promise.any()
静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise
。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError
拒绝。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| Promise.any = function(iterable) { let arr = [...iterable].map(item => item instanceof Promise ? item : Promise.resolve(item)) if(arr.length === 0) return Promise.reject('All promise rejected') return new Promise((resolve, reject) => { let rejectCount = 0 for(let i=0; i<arr.length; i++) { arr[i].then(resolve, reason => { rejectCount++ if(rejectCount === arr.length) { reject('All promises rejected') } }) } }) }
let p1 = new Promise(r => setTimeout(r, 3000, 1)) let p2 = new Promise((r,j) => setTimeout(j, 1000, 2)) let p3 = new Promise(r => setTimeout(() => r(3), 500)
Promise.any([p1, p2, p3]) .then(data => console.log(data)) .catch(e => console.error(e))
Promise.any('hello').then(data => console.log(data))
Promise.any('').then(data => console.log(data), reason => console.error(reason))
Promise.any([Promise.resolve(2), 3, Promise.reject(4)]).then(data => console.log(data))
|
手写并发控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| function asyncPool(fn, arr, limit = 2) { let args = [...arr] let currentCount = 0 let results = [] let settledCount = 0 let order = 0
return new Promise((resolve, reject) => {
function run() { while (currentCount < limit && args.length > 0) { currentCount++
(function (i) { console.log('当前请求数' + currentCount) let val = args.shift() fn(val).then(v => { results[i] = v }).finally(() => { settledCount++ currentCount-- if (settledCount === arr.length) { resolve(results) } else { run() } }) })(order++)
} }
run()
}) }
function getWeather(city) { console.log(`开始获取${city}的天气`) return fetch(`http://api2.jirengu.com/getWeather.php?city=${city}`).then(res => res.json()) }
let citys = ['北京', '上海', '杭州', '成都', '武汉', '天津', '深圳', '广州', '合肥', '郑州'] asyncPool(getWeather, citys, 5).then(results => console.log(results))
|

宏任务、微任务原理
运行顺序
- 先运行同步代码
- 再扫描微队列依次运行并清空全部任务
- 扫描宏队列拿出一个任务运行
- 再扫描微队列依次运行并清空全部任务
- 扫描宏队列拿出一个任务运行…..
- 直到两个队列都为空
同步与放入宏队列
setTimeout(fn, t)
本身是同步代码,作用是创建一个定时器
t
毫秒后把fn
放入宏队列
new Promise(function fn(resolve, reject) { ...})
会立即运行fn
,其中**fn
里也是同步代码**
放入微队列
- 对于处于
pending
状态的Promise
对象p
,内部状态的resolve
,才会触发p.then(fn)
中的fn
加入微队列
- 对于处于
fulfilled
状态的Promise
对象p
, 执行p.then(fn)
会立即让fn
加入微队列

宏任务、微任务案例1
原始代码,输出什么?
1 2 3 4 5 6 7 8 9
| setTimeout(() => console.log(1), 0); new Promise(resolve => { resolve(); console.log(2); }).then(() => { console.log(3); }); console.log(4);
|
改造后,所有的函数都加个名字,便于分析
1 2 3 4 5 6 7 8 9 10
| setTimeout(function f1( { // 1. setTimeout本身是同步代码,作用是创建一个定时器(宏任务) console.log(1) }, 0); new Promise(function f2(resolve) { resolve(); console.log(2); }).then(function f3() { console.log(3); }); console.log(4);
|
执行过程:
- 执行同步代码创建计时器,并立即加入到宏队列。 此时宏队列【f1】,微队列【】;
- 创建Promise,运行同步代码f2()。运行resolve()会触发f3加入微队列。此时宏队列【f1】,微队列【f3】。再输出2;
- 运行同步代码输出4。同步代码执行完毕,扫描微队列,执行全部任务,执行f3(),输出3;
- 拿出一个宏队列任务,执行f1(),输出1。此时队列都为空。


宏任务、微任务案例2
输出什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| new Promise(function f1(resolve){ console.log(1); setTimeout(function f2() { console.log(2); }); resolve(1); }).then(function f3(res) { console.log(3); })
setTimeout(function f4() { console.log(4); })
console.log(5);
|
- 执行同步代码创建Promise对象,运行同步代码f1()。输出1,创建定时器,立即把f2加入宏队列。运行resolve(1),触发f3加入微队列。此时宏队列【f2】,微队列【f3】;
- 创建定时器,立即把f4加入宏队列。此时宏队列【f2, f4】,微队列【f3】;
- 运行同步代码输出5。同步代码执行完毕;
- 扫描微队列,拿出并执行全部任务,执行f3(),输出3;
- 拿出一个宏队列任务,执行f2(),输出2。扫描微队列为空,再扫描宏队列拿出f4执行,输出4。此时队列都为空。


宏任务、微任务案例3
输出什么?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| setTimeout(function f1() { console.log(1) })
new Promise(function f2(resolve) { resolve() console.log(2) }).then(function f3() { console.log(3) Promise.resolve().then(function f4() { console.log(4) }).then(function f5() { Promise.resolve().then(function f6() { console.log(5) }) }) })
console.log(6)
|
- 创建定时器,立即把f1加入宏队列。此时宏队列【f1】,微队列【】;
- 创建Promise对象,执行同步代码f2()。执行resolve() 触发f3加入微队列。之后输出2。此时宏队列【f1】,微队列【f3】;
- 运行同步代码输出6。同步代码执行完毕;
- 扫描微队列,拿出并执行全部任务。先执行f3(),输出3;Promise.resolve().then(f4) 立即把f4加入微队列。此时宏队列【f1】,微队列【f4】。微队列不为空,拿出f4执行,输出4。f4执行完导致当前状态fulfilled,触发f5加入微队列。拿出f5继续执行,触发f6立即加入微队列。拿出并执行f6,输出5。此时宏队列为【f1】,微队列为空
- 扫码宏队列,拿出运行f1,输出1。


宏任务、微任务案例4
1 2 3 4 5 6 7 8 9 10 11 12 13
| console.log(1) setTimeout(function f1(){ console.log(2) Promise.resolve().then(function f2() { console.log(3) setTimeout(function f3() { console.log(4) }) }).then(function f4() { console.log(5) }) }, 0) console.log(6)
|
- 输出1;
- 创建定时器,立即把f1加入宏队列。此时宏队列【f1】,微队列【】;
- 输出6;
- 扫描微队列,为空。扫描宏队列,取出f1运行。输出2;f2立即加入微队列。此时宏队列【】,微队列【f2】。
- 扫描微队列,拿出并执行全部任务。执行f2(),输出3;创建计时器,立即把f3加入宏队列。f2执行完毕时导致当前promise fulfilled,触发f4加入微队列。此时宏队列【f3】,微队列【f4】。微队列不为空,继续拿出f4执行,输出5。此时宏队列【f3】,微队列【】
- 扫码宏队列,运行f3,输出4。结束。

