promise是一个在面试中很常见的知识,这篇文章研究了手写实现一个promise,并实现Promise池并发,借此和各位读者一起重温/学习这个 JS 中的重要内容。
Proimse
实现promise ,Promise.race() ,Promise.allSettle() , Promise.all() ,promiseInstence.then()
分析原生Promise调用
const promise1 = new Promise((resovle,reject)=>{})
//作为一个类,传入一个回调函数,回调函数的参数是Promise的内部方法
p1.then(res=>{}).then().then().then()
//对实例调用then方法,传入一个回调函数,接受一个result|reason
//proimse实例的状态落定之后会执行then传入的回调函数
提出需求和对策
-
promise类接受一个executor回调参数
-
接受两个回调,由Promise内部提供
-
-
promise实例上有then方法
-
then方法能够链式调用
-
then方法中的回调函数在promise落定的时候被调用
-
then方法执行后value时then中回调返回的方法(resolve)
-
-
构造函数接受一个executor参数
- 在constructor中定义reject和resolve,传入执函数
-
promise类上挂载then方法,接受两个回调函数
-
then方法返回一个promise,
-
这个promise根据上一个promise的状态执行回调或者未落定时推入任务队列,任务队列在原promise落定时被执行
-
then方法返回的promise的executor中执行resolve(fullFilled(this.value)) || this.value)
-
export class myPromise {
constructor(executor) {
this.status = 'pending';
this.value = undefined;
this.reason = undefined;
this.onFulfilledCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === 'pending') {
this.status = 'resolved';
this.value = value;
this.onFulfilledCallbacks.forEach(fn => fn());
}
};
let reject = (reason) => {
if (this.status === 'pending') {
this.status = 'rejected';
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
return new myPromise((resolve, reject) => {
if (this.status === 'resolved') {
try {
let x = onFulfilled(this.value);
resolve(x || this.value);
} catch (e) {
reject(e);
}
}
if (this.status === 'rejected') {
try {
let x = onRejected(this.reason);
resolve(x || this.reason);
} catch (e) {
reject(e);
}
}
if (this.status === 'pending') {
this.onFulfilledCallbacks.push(() => {
try {
let x = onFulfilled(this.value);
resolve(x || this.value);
} catch (e) {
reject(e);
}
});
this.onRejectedCallbacks.push(() => {
try {
let x = onRejected(this.reason);
resolve(x || this.reason);
} catch (e) {
reject(e);
}
});
}
});
}
}
Promise.prototype.finally
- finally的回调没有参数,也就无法访问then方法的返回值
- finally回调如果返回一个Promise,那么等待Promise成功,返回上一个Promise的值(成功或者失败)
- 如果finally回调返回的Promise失败,那么返回的值是此Promise失败的值
原理是,finnally返回的是一个proimse,这个proimse只有的then方法只有成功回调,没有失败回调
finnally(callback){
return this.then(
(result )=>{return Promise.resolve(callback).then(()=>return result )},
(reason )=>{return Promise.resolve(callbakc).then(()=>return reason )}
)
}
Promise同步
- 使用链式调用 (then)
- 或者使用async await
Promise并发
-
使用类,类内部记录
- 当前执行的数量
- 已经完成的数量
- promiseList的长度
- promiseList的map(添加index用于保证result有序)
- result 保序的结果
-
run函数 执行的主函数
-
判断现在能够执行多少个promise,从promiseList中拿出
-
Promse执行完后的then添加结果到then
-
其finally将当前执行数 -- ,并触发run函数再次执行
- 当所有的请求都完成的时候,触发getResult函数的PromiseResolve将结果返回 Resolve(this.result)
-
-
getResult函数将resolve暴露到全局,供finally触发,返回
下面是用类/函数两种 方法实现 Promise 并发
// 核心逻辑 1 调度器在promise落定的时候递归,计算当前能够执行的个数
// 核心逻辑 2 PromisePool的状态所有promise落定的时候改变,返回所有result组成的数组
class PromisePool {
constructor(maxConcurrent, promiseList) {
this.maxConcurrent = maxConcurrent
this.promiseList = promiseList.map((item, index) => {
return { item, index }
})
this.promiseLength = promiseList.length
this.runningCount = 0
this.settleCount = 0
this.allSettle = undefined
this.result = []
this.promise = new Promise(resolve => {
this.allSettle = resolve
})
}
run() {
const readyToRun = Math.min(
this.promiseList.length,
this.maxConcurrent - this.runningCount
)
for (let i = 0; i < readyToRun; i++) {
const target = this.promiseList.shift()
this.runningCount++
target
.item()
.then(res => {
this.result[target.index] = res
})
.catch(e => {
this.result[target.index] = e
})
.finally(() => {
this.settleCount++
this.runningCount--
if (this.settleCount === this.promiseLength) {
this.allSettle(this.result)
} else {
this.run()
}
})
}
return this.promise
}
}
function concurrentPromsie(maxConcurrent, promiseList) {
const proimseLength = promiseList.length
promiseList = promiseList.map((item, index) => {
return { item, index }
})
let runningCount = 0
let settleCount = 0
let allsettle = null
let result = []
let promise = new Promise(resolve => {
allsettle = resolve
})
function run() {
const readyToRun = Math.min(promiseList.length, maxConcurrent - runningCount)
for (let i = 0; i < readyToRun; i++) {
runningCount++
const target = promiseList.pop()
target
.item()
.then(res => {
result[target.index] = res
})
.catch(e => {
result[target.index] = e
})
.finally(() => {
runningCount--
settleCount++
if (settleCount === proimseLength) {
allsettle(result)
} else {
run()
}
})
}
return promise
}
return run()
}
const func1 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func1')
resolve(1)
}, 2000)
})
const func2 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func2')
resolve(2)
}, 1000)
})
const func3 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func3')
resolve(3)
}, 1000)
})
const func4 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func4')
resolve(4)
}, 1000)
})
const func5 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func5')
resolve(5)
}, 1000)
})
const func6 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func6')
resolve(6)
}, 1000)
})
const func7 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func7')
resolve(7)
}, 1000)
})
const func8 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func8')
resolve(8)
}, 1000)
})
const func9 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func9')
resolve(9)
}, 1000)
})
const func10 = async () =>
new Promise(resolve => {
setTimeout(() => {
console.log('func10')
resolve(10)
}, 1000)
})
// const promisePool = new PromisePool(3, [
// func1,
// func2,
// func3,
// func4,
// func5,
// func6,
// func7,
// func8,
// func9,
// func10,
// ])
// promisePool.run().then(res => console.log(res))
concurrentPromsie(2, [
func1,
func2,
func3,
func4,
func5,
func6,
func7,
func8,
func9,
func10,
]).then(res => console.log(res))
笔者才疏学浅,各位读者多多担待,不吝赐教。