Promise
promise状态:
Pending(进行中)、Fulfilled(已成功)、Rejected(已失败)
promise基本使用:
new Promise((resolve,reject)=>{...})
.then(success=>{...})
.catch(error=>{...})
.finally(()=>{...});
Promise.resolve();
Promise.reject();
Promise.all();
Promise.race();
promise需要注意的地方:
1.then(success=>{},error=>{})中的error回调可以捕获reject结果,但是不能捕获success回调函数中的异常,所以我们推荐使用catch()函数捕获reject结果
2.then().then()的调用链中return 新的promise对象会导致执行顺序的改变,如:
const p1 = new Promise((resolve, reject) => {
console.log('promise1');//1
resolve();
})
.then(() => {
console.log('then11');//3
new Promise((resolve, reject) => {
console.log('promise2');//4
resolve();
})
.then(() => {
console.log('then21');//6
})
.then(() => {
console.log('then23');//8
});
})
.then(() => {
console.log('then12');//7
});
const p2 = new Promise((resolve, reject) => {
console.log('promise3');//2
resolve();
}).then(() => {
console.log('then31');//5
});
const p1 = new Promise((resolve, reject) => {
console.log('promise1'); // 1
resolve();
})
.then(() => {
console.log('then11'); // 2
return new Promise((resolve, reject) => {
console.log('promise2'); // 3
resolve();
})
.then(() => {
console.log('then21'); // 4
})
.then(() => {
console.log('then23'); // 5
});
})
.then(() => {
console.log('then12'); //6
});
3.promise.then()期望参数是一个函数,如果不是函数就会发生值透传。就算发生了透传,promise.then()中的代码依旧也是会执行的。
基本使用
Promise.all([])
当数组中的所有 Promise 实例都成功时,它会按照请求的顺序返回成功结果数组。如果任何 Promise 失败,它就会进入失败回调。
const p1 = new Promise((resolve) => {resolve(1);});
const p2 = new Promise((resolve) => {resolve(1);});
const p3 = Promise.resolve('ok');
// If all promises succeed, result will be an array of 3 results.const result = Promise.all([p1, p2, p3]);
// If one fails, the result is the failed promise's value
Promise.allSettled([])
执行不会失败,它返回一个与输入数组中每个 Promise 实例的状态相对应的数组。
const p1 = Promise.resolve(1);
const p2 = Promise.reject(-1);
Promise.allSettled([p1, p2]).then(res => {
console.log(res);
});
// Output:/* [ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: -1 } ]*/
Promise.any([])
如果输入数组中的任何 Promise 状态变为 fulfilled,则返回的实例将变为 fulfilled 状态并返回第一个 fulfilled 的 Promise 的值。如果全部被拒绝,那么最终的状态就为拒绝。
const p1 = new Promise((resolve, reject) => {
reject(1);
});
const p2 = new Promise((resolve, reject) => { reject(2);});
const p3 = Promise.resolve("ok");
Promise.any([p1, p2, p3]).then((r) => console.log(r));
Promise.race([])
一旦数组中任何一个Promise的状态发生改变,race方法的状态就会随之改变;第一个改变的Promise的值将被传递给race方法的回调。
const p1 = new Promise((resolve) => {
setTimeout(() => {
resolve(10);
}, 3000);});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
throw new Error("I encountered an error");
}, 2000);
});
Promise.race([p1, p2]).then((v) => console.log(v));
抛出异常并不会改变 race 状态;最终状态仍然由 p1 决定。
高阶技巧
以下 9 个高阶技巧,可帮助开发人员更高效、更优雅地处理异步操作。
- 并发控制
使用 Promise.all 可以并行执行多个 Promises,另外可以实现并发控制函数来控制同时请求的数量。
const concurrentPromises = (promises, limit) => {
return new Promise((resolve, reject) => {
let i = 0;
let result = [];
const executor = () => {
if (i >= promises.length) {
return resolve(result);
}
const promise = promises[i++];
Promise.resolve(promise)
.then(value => {
result.push(value);
if (i < promises.length) {
executor();
} else {
resolve(result);
}
})
.catch(reject);
};
for (let j = 0; j < limit && j < promises.length; j++) {
executor();
}
});
};
- Promise Timeout
有时,我们希望如果 Promise 在一定时间范围内一直未决,则自动拒绝。可以按如下方式实现。
const promiseWithTimeout = (promise, ms) =>
Promise.race([promise,
new Promise((resolve, reject) => setTimeout(() => reject(new Error('Timeout after ' + ms + 'ms')), ms))
]);
- 可取消的Promise
原生 JavaScript Promises 无法取消,但你可以通过引入可控的中断逻辑来模拟取消。
const cancellablePromise = promise => {
let isCanceled = false;
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
value => (isCanceled ?
reject({ isCanceled, value })
: resolve(value)),
error => (isCanceled ? reject({ isCanceled, error })
: reject(error))
);
});
return {
promise: wrappedPromise,
cancel() {
isCanceled = true;
}
};
};
- 按顺序执行 Promise 数组
有时我们需要按顺序执行一系列 Promises,以确保前一个异步操作完成后再开始下一个操作。
const sequencePromises = promises =>
promises.reduce(
(prev, next) => prev.then(() => next()),
Promise.resolve()
);
- Promises 重试
当 Promise 由于临时错误而被拒绝时,我们可能想要重试其执行。
const retryPromise = (promiseFn, maxAttempts, interval) => { return new Promise((resolve, reject) => {
const attempt = attemptNumber => {
if (attemptNumber === maxAttempts) {
reject(new Error('Max attempts reached'));
return;
}
promiseFn().then(resolve).catch(() => {
setTimeout(() => {
attempt(attemptNumber + 1);
}, interval);
});
};
attempt(0);
});
};
- 确保 Promise 仅 Resolve 一次
在某些情况下,可能希望确保 Promise 只 resolve 一次,即使多次调用 resolve。
const onceResolvedPromise = executor => {
let isResolved = false;
return new Promise((resolve, reject) => {
executor(
value => {
if (!isResolved) {
isResolved = true;
resolve(value);
}
},
reject
);
});
};
- 使用 Promises 代替回调
Promise 已经完全替代回调函数,成为一种更标准化、更便捷的处理异步操作方式。
const callbackToPromise = (fn, ...args) => {
return new Promise((resolve, reject) => {
fn(...args, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
- 动态生成 Promise 链
在某些情况下,可能需要根据不同的条件动态创建一系列 Promise 链。
const tasks = [task1, task2, task3]; // Array of asynchronous tasks
const promiseChain = tasks.reduce((chain, currentTask) => {
return chain.then(currentTask);
}, Promise.resolve());
- 使用 Promises 实现简单的异步锁
在多线程环境中,可以使用 Promises 实现简单的异步锁,从而确保一次只有一个任务可以访问共享资源。
let lock = Promise.resolve();
const acquireLock = () => {
let release;
const waitLock = new Promise(resolve => {
release = resolve;
});
const tryAcquireLock = lock.then(() => release);
lock = waitLock;
return tryAcquireLock;
};
此代码不断创建和解析 Promises,实现一个简单的 FIFO 队列,以确保只有一个任务可以访问共享资源。lock 变量代表当前是否有任务正在执行,始终指向正在进行的任务的 Promise。acquireLock 函数用于请求执行权限并创建一个新的 Promise 来等待当前任务完成。
参考:
从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节
Promise.any 的作用,如何自己实现一个 Promise.any
Promise.allSettled 的作用,如何自己实现一个 Promise.allSettled
【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理)
Promise不会??看这里!!!史上最通俗易懂的Promise!!!
Promise 被玩出 48 种“花样”,深度解析10个常用模块
[译]JavaScript: Promises 介绍及为何 Async/Await 最终取得胜利
嘿,不要给 async 函数写那么多 try/catch 了