手写promise
思路:
- promise有状态pending、fulfilled和rejected,所以有个变量来保存状态
- pending:准备(等待中)
- fulfilled:成功
- rejected:失败
- 需要注意:promise的状态,一旦改变,就不会再变
- 需要判断一下,如果为pending的时候,才进行改变,否则状态不能改变
- 当出现异常代码错误的时候需要抛出异常而且promise状态应该为reject,使用try/catch捕获异常,设置promise的状态为reject,
- primise最重要的方法是then,需要实现then方法,需要接受2个回调函数分别是resolved和rejected
- 由于then方法是异步方法,需要放到任务队列里,可以放到定时器里
- 需要有一个resolve的回调函数集的状态,定义一个resolve回调函数集数组,需要有一个reject的回调函数集的状态,定义一个reject回调函数集数组
- 当resolve和reject被调用的时候,才执行resolve和reject的回调函数集
简易版promise
- 1、实现resolve和reject方法
- 2、实现then的方法 、实现了定时器情况
- 暂未实现链式调用、微任务
/**
* promise有三种状态:pending(等待),fulfilled(成功),rejected(失败)
*/
class MyPromise {
// 构造方法,初始化调用
constructor(executor) {
// 初始化值
this.status = "pending"; // 状态, pending等待, 初始值
this.value = null; // 结果值
/**
* 一个promise实例可能会多次then,用数组就一个一个保存
*/
this.onFulfilledCallback = []; // resolve时的回调函数集
this.onRejectedCallback = []; // reject时的回调函数集
// 成功值 【success value】
const resolve = (value) => {
/**
* Promise状态一旦改变,就不会再变
* Promise只以第一次为准,第一次成功就永久为fulfilled,第一次失败就永远状态为rejected
* 需要加个判断,以第一次状态为准
*/
if (this.status !== "pending") return;
// 如果执行resolve,状态变为fulfilled
this.status = "fulfilled";
// 值为传进来的value
this.value = value;
while (this.onFulfilledCallback.length) {
this.onFulfilledCallback.shift()(this.value);
}
};
// 失败原因 【failure reason】
const reject = (reason) => {
if (this.status !== "pending") return;
// 如果执行reject,状态变为rejected
this.status = "rejected";
// 值为传进来的reason
this.value = reason;
while (this.onRejectedCallback.length) {
this.onRejectedCallback.shift()(this.value);
}
};
try {
executor(resolve, reject);
} catch (err) {
this.reject(err);
}
}
/**
* then特性
* 1、then接收两个回调,一个是成功回调,一个是失败回调
* 2、当Promise状态为fulfilled执行成功回调,为rejected执行失败回调
* 3、如resolve或reject在定时器里,则定时器结束后再执行then
* 4、支持链式调用, 返回一个新的promise对象 【下一次then执行受上一次then返回值的影响】
*/
then(fulfilledCallback, rejectedCallback) {
// 参数校验,确保一定是函数
fulfilledCallback =
typeof fulfilledCallback === "function"
? fulfilledCallback
: (val) => val;
rejectedCallback =
typeof rejectedCallback === "function"
? rejectedCallback
: (reason) => {
throw reason;
};
// promise有三种状态:pending(等待),fulfilled(成功),rejected(失败)
const actionMap = {
fulfilled: () => {
// 如果当前为成功状态,执行第一个回调
fulfilledCallback(this.value);
},
rejected: () => {
// 如果当前为失败状态,执行第二哥回调
rejectedCallback(this.value);
},
pending: () => {
/**
* 1、处理定时器情况,定时器结束后再执行then
* 2、如果定时器还没走完,状态还是pending等待状态
* 3、如果状态为等待状态,暂时保存两个回调
*/
this.onFulfilledCallback.push(fulfilledCallback);
this.onRejectedCallback.push(rejectedCallback);
}
};
if (Object.keys(actionMap).includes(this.status)) {
actionMap[this.status]();
}
}
}
测试:
const test1 = new MyPromise((resolve, reject) => {
resolve("成功");
}).then((res) => {
console.log(res, "res");
});
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("1s之后执行");
}, 1000);
}).then((res) => {
console.log(res, "res");
});
console.log(test1);
console.log(test2);
实现定时器情况
1、处理定时器情况,定时器结束后再执行then 2、如果定时器还没走完,状态还是pending等待状态【初始状态】 3、如果状态为等待状态,暂时保存两个回调
实现then链式调用
1、then支持链式调用,下一次then执行受上一次then返回值的影响
特点:
1、then方法本身会返回一个新的Promise对象
2、如果返回值是promise对象,返回值为成功,新promise就是成功
3、如果返回值是promise对象,返回值为失败,新promise就是失败
4、如果返回值非promise对象,新promise对象就是成功,值为此返回值
/**
* then特性
* 1、then接收两个回调,一个是成功回调,一个是失败回调
* 2、当Promise状态为fulfilled执行成功回调,为rejected执行失败回调
* 3、如resolve或reject在定时器里,则定时器结束后再执行then
* 4、支持链式调用, 返回一个新的promise对象 【下一次then执行受上一次then返回值的影响】
*/
then(fulfilledCallback, rejectedCallback) {
// 参数校验,确保一定是函数
fulfilledCallback =
typeof fulfilledCallback === "function"
? fulfilledCallback
: (val) => val;
rejectedCallback =
typeof rejectedCallback === "function"
? rejectedCallback
: (reason) => {
throw reason;
};
const thenPromise = new MyPromise((resolve, reject) => {
const resolvePromise = (cb) => {
try {
const result = cb(this.value);
/**
* 1、then返回了promise对象
* 2、如果返回值是promise对象,返回值为成功,新promise就是成功
* 3、如果返回值是promise对象,返回值为失败,新promise就是失败
* 4、谁知道返回的promise是失败成功?只有then知道
*/
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
throw new Error(err);
}
};
// promise有三种状态:pending(等待),fulfilled(成功),rejected(失败)
const actionMap = {
fulfilled: () => {
// 如果当前为成功状态,执行第一个回调
resolvePromise(fulfilledCallback);
},
rejected: () => {
// 如果当前为失败状态,执行第二哥回调
resolvePromise(rejectedCallback);
},
pending: () => {
/**
* 1、处理定时器情况,定时器结束后再执行then
* 2、如果定时器还没走完,状态还是pending等待状态
* 3、如果状态为等待状态,暂时保存两个回调
*/
this.onFulfilledCallback.push(
resolvePromise.bind(this, fulfilledCallback)
);
this.onRejectedCallback.push(
resolvePromise.bind(this.onRejectedCallback)
);
}
};
if (Object.keys(actionMap).includes(this.status)) {
actionMap[this.status]();
}
});
return thenPromise;
}
测试:
const test = new MyPromise((resolve, reject) => {
resolve("成功");
})
.then((res, err) => {
console.log(res, "res");
return new MyPromise((resolve, reject) => {
reject("then1");
});
})
.then((res, error) => {
console.log(error, "reject");
console.log(res, "获取上一个then返回结果");
});
console.log(test);
实现then的微任务
-
then方法是
微任务,需要给promises实现这个特性 -
可以暂时使用定时器来模拟异步,使用 setTimeout 代替微任务的异步效果,只是为了模拟那种效果,不是真的代替微任务
-
只需要让
resolvePromise函数异步执行就可以了
const resolvePromise = (cb) => {
setTimeout(() => {
try {
const result = cb(this.value);
/**
* 1、then返回了promise对象
* 2、如果返回值是promise对象,返回值为成功,新promise就是成功
* 3、如果返回值是promise对象,返回值为失败,新promise就是失败
* 4、谁知道返回的promise是失败成功?只有then知道
*/
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (err) {
reject(err);
throw new Error(err);
}
});
};
实现Promise.resolve
Promsie.resolve(value) 可以将任何值转成值为 value 状态是 fulfilled 的 Promise,但如果传入的值本身是 Promise 则会原样返回它
Promise.resolve = function(value) {
// 如果是 Promsie,则直接输出它
if(value instanceof Promise){
return value
}
return new Promise(resolve => resolve(value))
}
实现Promise.reject
和 Promise.resolve() 类似,Promise.reject() 会实例化一个 rejected 状态的 Promise。但与 Promise.resolve() 不同的是,如果给 Promise.reject() 传递一个 Promise 对象,则这个对象会成为新 Promise 的值。
Promise.reject = function(reason) {
return new Promise((resolve, reject) => reject(reason))
}
实现Promise.all
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 如果所有Promise都成功,则返回成功结果数组
- 如果有一个Promise失败,则返回这个失败结果
Promise.all = function(promiseArr) {
let index = 0, result = []
return new Promise((resolve, reject) => {
promiseArr.forEach((p, i) => {
Promise.resolve(p).then(val => {
index++
result[i] = val
if (index === promiseArr.length) {
resolve(result)
}
}, err => {
reject(err)
})
})
})
}
实现Promise.race
- 接收一个Promise数组
- 数组里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
Promise.race = function(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(val => {
resolve(val)
}, err => {
rejecte(err)
})
})
})
}
如何实现并发请求数量控制
第三方库: https://github.com/rxaviers/async-pool
参考:
1、https://juejin.cn/post/7057026063225847821
2、https://zhuanlan.zhihu.com/p/455838344
方式1 【Promise.race】
-
使用 Promise.race 判断是否已达到并发的最大数量。依次遍历数据,将异步请求放入并发池中,当并发池中的任务数量达到最大值时,通过 Promise.race 等待并发池中的任务完成,每完成一个任务,就将该任务从并发池中移除,然后将下一个数据的请求任务放入并发池中,直到所有任务都执行完成
-
设置一个合理的超时值来限制请求的执行时间,并通过使用
Promise.race方法来确定哪个请求先完成。为了防止请求无限期地等待响应,从而导致程序卡住。如果没有超时时间的限制,那么即使服务器无法响应,请求也会一直等待下去
在超过超时时间后,如果还没有收到响应,就会触发超时错误。
核心步骤:
1、设置一个并发请求的数量的变量来控制
2、循环遍历url,将每一个url请求添加到数组中,每次循环都需要检查并发数是否已经达到了限制,如果达到了就调用就调用
Promise.race方法来等待一个请求完成,然后移除已经完成的请求3、最后,调用
Promise.all方法来等待所有请求完成。
例子:
// 并发请求的数量
const CONCURRENCY = 3;
async function sendRequest(url) {
// 设置超时时间为 5 秒
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Request timed out')), 5000);
});
// 发起请求
const response = request(url);
// 返回最先完成的请求
return Promise.race([timeout, response]);
}
async function main() {
const urls = [
'http://example.com/1',
'http://example.com/2',
'http://example.com/3',
'http://example.com/4',
'http://example.com/5'
];
const promises = [];
for (const url of urls) {
promises.push(sendRequest(url));
// 如果当前的并发数已经达到了限制,就等待一个请求完成
if (promises.length >= CONCURRENCY) {
await Promise.race(promises);
// 移除已经完成的请求
promises.splice(0, 1);
}
}
// 等待所有请求完成
await Promise.all(promises);
}