Promise
2)Promise优点
②Promise 与事件对比 和事件相比较, Promise 更适合处理一次性的结果。在结果计算出来之前或之后注册回调函数都是可以的,都可以拿到正确的值。 Promise 的这个优点很自然。但是,不能使用 Promise 处理多次触发的事件。链式处理是 Promise 的又一优点,但是事件却不能这样链式处理。 ③Promise 与回调对比 解决了回调地狱的问题,将异步操作以同步操作的流程表达出来。 ④Promise 带来的额外好处是包含了更好的错误处理方式(包含了异常处理),并且写起来很轻松(因为可以重用一些同步的工具,比如 Array.prototype.map() )。
3)Promise缺点
1、无法取消Promise,一旦新建它就会立即执行,无法中途取消。 2、如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。 3、当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。 4、Promise 真正执行回调的时候,定义 Promise 那部分实际上已经走完了,所以 Promise 的报错堆栈上下文不太友好。
/**
* 1. new Promise时,需要传递一个 executor 执行器,执行器立刻执行
* 2. executor 接受两个参数,分别是 resolve 和 reject
* 3. promise 只能从 pending 到 rejected, 或者从 pending 到 fulfilled
* 4. promise 的状态一旦确认,就不会再改变
* 5. promise 都有 then 方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled,
* 和 promise 失败的回调 onRejected
* 6. 如果调用 then 时,promise已经成功,则执行 onFulfilled,并将promise的值作为参数传递进去。
* 如果promise已经失败,那么执行 onRejected, 并将 promise 失败的原因作为参数传递进去。
* 如果promise的状态是pending,需要将onFulfilled和onRejected函数存放起来,等待状态确定后,再依次将对应的函数执行(发布订阅)
* 7. then 的参数 onFulfilled 和 onRejected 可以缺省
* 8. promise 可以then多次,promise 的then 方法返回一个 promise
* 9. 如果 then 返回的是一个结果,那么就会把这个结果作为参数,传递给下一个then的成功的回调(onFulfilled)
* 10. 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调(onRejected)
* 11.如果 then 返回的是一个promise,那么需要等这个promise,那么会等这个promise执行完,promise如果成功,
* 就走下一个then的成功,如果失败,就走下一个then的失败
*/
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class Promise {
constructor(executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
// 存放成功/失败的队列
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
const resolve = (value) => {
// 如果value是一个Promise 递归解析
if (value instanceof Promise) {
return value.then(resolve, reject)
}
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFulfilledCallbacks.forEach(fn => fn())
}
}
const 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) {
// 判断 onFulfilled, onRejected 是否是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
let newPromise = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
// resolvePromise(newPromise, x, resolve, reject)
// 简单的
if(x instanceof Promise){
x.then(
value=> resolve(value),
reason=> reject(reason)
)
}else{
resolve(x)
}
} catch (error) {
reject(error)
}
})
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason)
// resolvePromise(newPromise, x, resolve, reject)
if(x instanceof Promise){
x.then(
value=> resolve(value),
reason=> reject(reason)
)
}else{
resolve(x)
}
} catch (error) {
reject(error)
}
})
}
if (this.status === PENDING) {
this.onFulfilledCallbacks.push((() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
// resolvePromise(newPromise, x, resolve, reject)
if(x instanceof Promise){
x.then(
value=> resolve(value),
reason=> reject(reason)
)
}else{
resolve(x)
}
} catch (error) {
reject(error)
}
})
}))
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
// resolvePromise(newPromise, x, resolve, reject)
if(x instanceof Promise){
x.then(
value=> resolve(value),
reason=> reject(reason)
)
}else{
resolve(x)
}
} catch (error) {
reject(error)
}
})
})
}
})
return newPromise
}
/**
* Promise.catch
* @description 用于promise方法链示,捕获前面 onFulfilled/onRejected 抛出的异常
* @param {*} errorCallback
*/
catch(errorCallback) {
return this.then(null, errorCallback)
}
/**
* Promise.finally
* @description finally 传入的函数 无论成功和失败都执行
* @param {*} callback 回调函数
* @returns 返回成功/失败
*/
finally(callback) {
return this.then((value) => {
// 返回上一次的值
return new Promise(callback()).then(() => value)
}, error => {
return new Promise(callback()).then(() => { throw error })
})
}
/**
* Promise.all
* @description 当这个数组里的所有promise对象全部变为resolve状态的时候,才会resolve 当有一个promise对象变为reject状态时 直接 reject
* @param {*} values promise对象组成的数组作为参数
* @returns 返回一个promise实例
*/
static all(values) {
return new Promise((resolve, reject) => {
let resultArr = []
let count = 0
values.forEach((promise, index) => {
promise.then((value) => {
resultArr[index] = value
if (++count === values.length) {
resolve(resultArr)
}
}, reject)
})
})
}
/**
* Promise.race
* @description 只要有一个promise对象进入FULFILLED 或者 REJECTED 状态的话,就会继续执行后面的处理
* @param {*} values 接受promise对象组成的数组作为参数
* @returns 返回一个Promise实例
*/
static race(values) {
return new Promise((resolve, reject) => {
values.forEach((promise) => {
promise.then(resolve, reject)
})
})
}
// 默认产生一个成功的promise
static resolve(value) {
return new Promise((resolve, reject) => {
resolve(value)
})
}
// 默认产生一个失败的promise
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
}
Promise.deferred = () => {
let defer = {}
defer.promise = new Promise((resolve, reject) => {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
const resolvePromise = (promise, x, resolve, reject) => {
if (x === promise) {
// If promise and x refer to the same object, reject promise with a TypeError as the reason.
reject(new TypeError('循环引用'))
}
// if x is an object or function,
if (x !== null && typeof x === 'object' || typeof x === 'function') {
// If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
let called
try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
let then = x.then // Let then be x.then
// If then is a function, call it with x as this
if (typeof then === 'function') {
// If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
// If/when rejectPromise is called with a reason r, reject promise with r.
then.call(x, y => {
if (called) return
called = true
resolvePromise(promise, y, resolve, reject)
}, r => {
if (called) return
called = true
reject(r)
})
} else {
// If then is not a function, fulfill promise with x.
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
// If x is not an object or function, fulfill promise with x
resolve(x)
}
}
promise 超时
// 封装一个严格任务函数,第一个参数为promise对象,第二个参数为判定的超时标准,默认3s
function strictTack(promise,delay = 3){
// 函数返回一个超时基准promise对象
let promiseTimeout = function(delay){
return new Promise((res,rej)=>{
setTimeout(()=>{
rej(new Error("运行超时!"))
},1000 * delay)
})
}
// race,参数数组内的promise并发执行,一旦其中有一个promise对象产生判决就会终止其余的promise对象
return Promise.race([promise,promiseTimeout(delay)])
}
// 异步任务p1
let p1 = new Promise((res,rej)=>{
setTimeout(()=>{
res("p1 was resoved")
},1000 * 2)
})
// 异步任务p2
let p2 = new Promise((res,rej)=>{
setTimeout(()=>{
res("p2 was resoved")
},1000 * 4)
})
strictTack(p1)
.then(e=>{
console.log(e)// p1 was resoved
}).catch(err=>{
console.log(err)
})
strictTack(p2)
.then(e=>{
console.log(e)
}).catch(err=>{
console.log(err) // Error:运行超时!
})
取消一个promise
function wrap(p) {
let obj = {};
let p1 = new Promise((resolve, reject) => {
obj.resolve = resolve;
obj.reject = reject;
});
obj.promise = Promise.race([p1, p]);
return obj;
}
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 2000);
});
let obj2 = wrap(promise);
obj2.promise.then(res => {
console.log(res);
});
// obj2.resolve("请求被拦截了");
// 在2秒内主动调用obj2.resolve,那么obj2.promise方法就会被替换成我们自己的方法,
isPromise
export function isPromise(value) {
return value && typeof value.subscribe !== 'function' && typeof value.then === 'function';
}
promiseAll和allSeleted
该Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。
相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。
Promise.all = function(promises) {
const values = []
let count = 0
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(res => {
count++
values[index] = res
if (count === promises.length) {
resolve(values)
}
}, err => {
reject(err)
})
})
})
}
Promise.allSeleted = function(promises) {
let count = 0
let result = []
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
Promise.resolve(promise).then(res => {
result[index] = {
value: res,
reason: null,
}
}, err => {
result[index] = {
value: null,
reason: err,
}
}).finally(() => {
count++
if (count === promises.length) {
resolve(result)
}
})
})
})
}
Promise.race
Promise.race = function (promises) {
return new Promise(((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then((data) => {
resolve(data);
return;
}, err=>reject(err));
}
}
));
}
一个可取消的 Promise.race,关键在于:
- 能在其中一个 Promise 先完成后,取消其他尚未完成的 Promise。
- 这在原生 JS 中要靠「AbortController」+「自定义逻辑」组合实现。
function cancelableRace(promises) {
const controllers = promises.map(() => new AbortController());
// 每个 promise 包装一层,加入中止控制
const wrapped = promises.map((p, index) => {
const controller = controllers[index];
const signal = controller.signal;
return new Promise((resolve, reject) => {
// 如果被取消,reject
signal.addEventListener("abort", () => {
reject(new Error("Aborted"));
});
// 继续原 promise 逻辑
p.then(resolve).catch(reject);
});
});
let canceled = false;
const cancel = () => {
if (canceled) return;
canceled = true;
controllers.forEach((c) => c.abort());
};
// 第一个 resolve/reject 后取消其他的
const race = Promise.race(wrapped).finally(cancel);
return { promise: race, cancel };
}
function delay(ms, value, shouldReject = false) {
return new Promise((resolve, reject) => {
setTimeout(() => {
shouldReject ? reject(value) : resolve(value);
}, ms);
});
}
const p1 = delay(1000, "one");
const p2 = delay(500, "two");
const p3 = delay(2000, "three");
const { promise, cancel } = cancelableRace([p1, p2, p3]);
promise
.then((res) => console.log("✅ Winner:", res))
.catch((err) => console.log("❌ Error:", err.message));
Promise.allSeleted 加缓存
const machTemList = [
() => require.async("../../../../packages/mach-common-template/index.js"),
() => require.async("../../../../packages/mach-user-template/index.js"),
() => require.async("../../../../packages/mach-mactivity-template/index.js"),
];
// mach模版分包加载状态上报
const machTemOwl = {};
// mach模版分包缓存
const machCache = new Map();
const handlePromise = (_req, _cache, i) => {
const _promise = Promise.resolve(_req());
_cache.set(i, () => _promise);
return _promise.then(res => {
_cache.set(i, () => res);
return {
status: "ful",
value: res
}
}).catch((e) => {
_cache.delete(i);
return ({
status: 'rej',
reason: e
})
})
};
const getMachTem = function (machTem, key) {
return Promise.all(machTem.map((function (req, i) {
if(!machCache.has(i)) {
return handlePromise(req, machCache, i);
} else {
return Promise.resolve(machCache.get(i)()).then(res => {
return {
status: "ful",
value: res
}
}).catch((e) => {
machCache.delete(i);
return handlePromise(req, machCache, i);
})
}
}))).then( (res) => {
try {
if (!machTemOwl[key]) {
// 只上报一次
machTemOwl[key] = true;
owlInstance.setMetric(key, 1, {
status: (res || []).map((_t) => (_t && _t.status || 'fulrej')).join(','),
version: VERSION,
keys: (res || []).map((_t) => (_t && _t.value ? Object.keys(isObj(_t.value) ? _t.value : {}).length : '-1')).join(','),
});
}
} catch (e) {}
return res.reduce((pre, cur) => cur.status === 'ful' ? Object.assign({}, pre, cur.value) : pre, {});
})
};
实现maxRequest,成功后resolve结果,失败后重试,尝试超过一定次数才真正的reject
function maxRequest(fn, maxNum) {
return new Promise((resolve, reject) => {
if (maxNum === 0) {
reject('max request number')
return
}
Promise.resolve(fn()).then(value => {
resolve(value)
}).catch(() => {
return maxRequest(fn, maxNum - 1)
})
})
}
红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
function light(cb, timer) {
return new Promise(resolve => {
setTimeout(() => {
cb();
resolve()
}, timer);
})
}
function step() {
Promise.resolve().then(() => {
return light(red, 3000)
}).then(() => {
return light(green, 2000)
}).then(() => {
return light(yellow, 1000)
}).finally(() => {
return step()
})
}
先并发请求 3 张图片,当一张图片加载完成后,又会继续发起一张图片的请求,让并发数保持在 3 个,直到需要加载的图片都全部发起请求。
var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/10/29/166be40ccc434be0~tplv-t2oaga2asx-image.image'];
function loadImg(url) {
return new Promise((resolve, reject) => {
const img = new Image()
img.onload = function () {
console.log('一张图片加载完成');
resolve();
}
img.onerror = reject
img.src = url
})
};
function limitLoad(urls, handler, limit) {
// 对数组做一个拷贝
const sequence = [].concat(urls)
let promises = [];
//并发请求到最大数
promises = sequence.splice(0, limit).map((url, index) => {
// 这里返回的 index 是任务在 promises 的脚标,用于在 Promise.race 之后找到完成的任务脚标
return handler(url).then(() => {
return index
});
});
// 利用数组的 reduce 方法来以队列的形式执行
return sequence.reduce((last, url, currentIndex) => {
return last.then(() => {
// 返回最快改变状态的 Promise
return Promise.race(promises)
}).catch(err => {
// 这里的 catch 不仅用来捕获 前面 then 方法抛出的错误
// 更重要的是防止中断整个链式调用
console.error(err)
}).then((res) => {
// 用新的 Promise 替换掉最快改变状态的 Promise
promises[res] = handler(sequence[currentIndex]).then(() => { return res });
})
}, Promise.resolve()).then(() => {
return Promise.all(promises)
})
}
limitLoad(urls, loadImg, 3)
/*因为 limitLoad 函数也返回一个 Promise,所以当 所有图片加载完成后,可以继续链式调用limitLoad(urls, loadImg, 3).then(() => { console.log('所有图片加载完成');}).catch(err => { console.error(err);})*/
并发请求控制
function loadImagesWithConcurrency(urls, concurrency = 3) {
let index = 0; // 当前待请求的URL索引
const total = urls.length;
let completed = 0; // 已完成的请求数
// 发起单个请求
function fetchNext() {
if (index >= total) return; // 所有URL都已发起请求
const url = urls[index++];
console.log(`发起请求: ${url}`);
fetch(url)
.then(response => {
if (!response.ok) throw new Error(`请求失败: ${url}`);
console.log(`加载成功: ${url}`);
})
.catch(error => {
console.error(`加载失败: ${error.message}`);
})
.finally(() => {
completed++;
// 每完成一个请求,就再发起一个新的,维持并发数
fetchNext();
// 所有请求都完成时的回调
if (completed === total) {
console.log("所有图片请求已完成");
}
});
}
// 初始启动并发请求
for (let i = 0; i < concurrency && i < total; i++) {
fetchNext();
}
}
// 示例用法
const imageUrls = [
"url1.jpg", "url2.jpg", "url3.jpg", "url4.jpg", "url5.jpg", "url6.jpg"
];
loadImagesWithConcurrency(imageUrls, 3);