更多文章可看专栏:juejin.cn/column/7423…
一、手写简单版Promise
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 存放成功的回调
this.onResolvedCallbacks = [];
// 存放失败的回调
this.onRejectedCallbacks= [];
let resolve = (value) => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 依次将对应的函数执行
this.onResolvedCallbacks.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 (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value)
}
if (this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value)
});
// 如果promise的状态是 pending,需要将 onFulfilled 和 onRejected 函数存放起来,等待状态确定后,再依次将对应的函数执行
this.onRejectedCallbacks.push(()=> {
onRejected(this.reason);
})
}
}
}
二、Promise.all
1) 核心思路
- 接收一个 Promise 实例的数组或具有 Iterator 接口的对象作为参数
- 这个方法返回一个新的 promise 对象,
- 遍历传入的参数,用Promise.resolve()将参数"包一层",使其变成一个promise对象
- 参数所有回调成功才是成功,返回值数组与参数顺序一致
- 参数数组其中一个失败,则触发失败状态,第一个触发失败的 Promise 错误信息作为 Promise.all 的错误信息。
2)实现代码
一般来说,Promise.all 用来处理多个并发请求,也是为了页面数据构造的方便,将一个页面所用到的在不同接口的数据一起请求过来,不过,如果其中一个接口失败了,多个请求也就失败了,页面可能啥也出不来,这就看当前页面的耦合程度了
function PromiseAll(promiseArr) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promiseArr)) {
return reject(new Error('arr'))
}
const res = []
let count = 0
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i]).then(value => {
count++
res[i] = value
if (count === promiseArr.length) {
resolve(res)
}
}).catch(e => reject(e))
}
})
}
const p1 = new Promise((res, rej) => {
setTimeout(() => {
res('p1')
}, 1000)
})
const p2 = new Promise((res, rej) => {
setTimeout(() => {
res('p2')
}, 2000)
})
const p3 = new Promise((res, rej) => {
setTimeout(() => {
res('p3')
}, 3000)
})
const test = PromiseAll([p2, p1, p3])
.then(res => console.log(res))
.catch(e => console.log(e))
console.log(test);
三、Promise.allSettled
function allSettled(promises) {
return new Promise((resolve, reject) => {
const results = [];
const len = promises.length;
let resolvedCount = 0;
for (let i = 0; i < len; i++) {
promises[i]
.then((value) => {
results[i] = { status: "fulfilled", value };
})
.catch((reason) => {
results[i] = { status: "rejected", reason };
})
.finally(() => {
resolvedCount++;
if (resolvedCount === len) {
resolve(results);
}
});
}
});
}
四、Promise.race
该方法的参数是 Promise 实例数组, 然后其 then 注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行. 因为 Promise 的状态只能改变一次, 那么我们只需要把 Promise.race 中产生的 Promise 对象的 resolve 方法, 注入到数组中的每一个 Promise 实例中的回调函数中即可
Promise.race = function (args) {
return new Promise((resolve, reject) => {
for (let i = 0; i < args.length; i++) {
args[i].then(resolve, reject)
}
})
}
五、Promise.any
使用场景
- 快速返回第一个成功的结果:当你有多个异步操作,并且只关心第一个成功的结果时,可以使用
Promise.any
。例如,多个 API 请求,只需要第一个成功的响应。 - 提高容错能力:在某些情况下,你可能有多个备选方案,只要有一个成功就可以继续处理,而不需要等待所有的操作都完成。
function promiseAny(promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return reject(new AggregateError([], 'All promises were rejected'));
}
let rejectedCount = 0;
const errors = [];
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then(resolve)
.catch(error => {
rejectedCount++;
errors[index] = error;
if (rejectedCount === promises.length) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
});
});
});
}
// Example usage
const promise1 = new Promise((resolve, reject) => setTimeout(reject, 100, 'Error 1'));
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'Result 2'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 300, 'Result 3'));
promiseAny([promise1, promise2, promise3])
.then(result => {
console.log(result); // "Result 2"
})
.catch(error => {
console.error(error);
});
六、Promise封装ajax请求
// promise 封装实现:
function myajax(url) {
// 创建一个 promise 对象
let promise = new Promise(function(resolve, reject) {
let xhr = new XMLHttpRequest();
// 新建一个 http 请求
xhr.open("GET", url, true);
// 设置状态的监听函数
xhr.onreadystatechange = function() {
if (this.readyState !== 4) return;
// 当请求成功或失败时,改变 promise 的状态
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
};
// 设置错误监听函数
xhr.onerror = function() {
reject(new Error(this.statusText));
};
// 设置响应的数据类型
xhr.responseType = "json";
// 设置请求头信息
xhr.setRequestHeader("Accept", "application/json");
// 发送 http 请求
xhr.send(null);
});
return promise;
}
七、Promise实现sleep函数
function timeout(delay) {
return new Promise(resolve => {
setTimeout(resolve, delay)
})
};
八、Promise错误间隔重试
function retry(promiseFn, times, interval) {
return new Promise((resolve, reject) => {
let count = 0;
function run() {
promiseFn()
.then(resolve)
.catch((error) => {
count++;
if (count === times) {
reject(error);
} else {
setTimeout(run, interval);
}
});
}
run();
});
}
九、Promise图片异步加载
let imageAsync=(url)=>{
return new Promise((resolve,reject)=>{
let img = new Image();
img.src = url;
img.οnlοad=()=>{
console.log(`图片请求成功,此处进行通用操作`);
resolve(image);
}
img.οnerrοr=(err)=>{
console.log(`失败,此处进行失败的通用操作`);
reject(err);
}
})
}
imageAsync("url").then(()=>{
console.log("加载成功");
}).catch((error)=>{
console.log("加载失败");
})
十、Promise实现红绿灯
const task = (timer, light) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (light === 'red') {
red()
}
else if (light === 'green') {
green()
}
else if (light === 'yellow') {
yellow()
}
resolve()
}, timer)
})
}
const step = () => {
task(3000, 'red')
.then(() => task(2000, 'green'))
.then(() => task(2100, 'yellow'))
.then(step)
}
step()
十一、Promise串行请求
// 6s后输出[1,2,3]
const funcArr = [
() =>
new Promise((resolve) => {
setTimeout(() => resolve(1), 2000);
}),
() =>
new Promise((resolve) => {
setTimeout(() => resolve(2), 1000);
}),
() =>
new Promise((resolve) => {
setTimeout(() => resolve(3), 3000);
}),
];
// 函数最终返回一个promise
function runPromiseByQueue(promiseFuncArr) {
const res = [];
return new Promise((resolve, reject) => {
promiseFuncArr
.reduce((acc, cur) => acc.then(cur).then((data) => res.push(data)), Promise.resolve())
// reduce函数最终返回一个promise,在onResolved这一步骤执行resolve,将结果输出
.then(() => resolve(res));
});
}
runPromiseByQueue(funcArr).then((res) => {
console.log(res);
});
十二、实现Promisify函数
function promisify(fn) {
return function(...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};
}
function exampleCallback(arg1, arg2, callback) {
setTimeout(() => {
if (arg1 > 0) {
callback(null, `Success: ${arg1 + arg2}`);
} else {
callback('Error: arg1 must be greater than 0');
}
}, 1000);
}
// 使用 promisify 转换
const examplePromise = promisify(exampleCallback);
// 调用并处理 Promise
examplePromise(1, 2)
.then(result => console.log(result)) // 输出: Success: 3
.catch(error => console.error(error));
examplePromise(0, 2)
.then(result => console.log(result))
.catch(error => console.error(error)); // 输出: Error: arg1 must be greater than 0