什么是 promise
- promise 是 JS 中进行异步变成的新的解决方案
- 具体表达
- 从语法功能上来说:promise 是一个构造函数
- 从功能上来说:promise 对象用来封装一个异步操作并可以获取其结果
- promise 的状态改变
- pending 变成 resolved(pending 待验证->已解决)
- pending 变成 rejected(pending 待定->被拒绝) 说明:只有这两种状态改变,并且一个 promise 对象只能改变一次 无论变成成功还是失败,都会有一个结果数据 成功的结果数据一般称为 value,失败的结果数据一般称为 reason
为什么要使用 promise
- 指定回调函数的方式更加灵活 旧的: 必须在启动异步任务前指定 promise: 启动异步任务=> 返回 promise 对象 =>给 promise 对象绑定回调函数
- 支持链式调用,可以解决回调地狱问题 promise 链式调用,终极解决方案是 async/await
promise和async/await的区别
- promise是ES6,async/await是ES7
- 相对于promise来讲,写法更加优雅
- reject状态
a. promise错误可以通过catch来捕捉,建议尾部捕获错误 b. async/await既可以用,.then又可以用try-catch捕捉
手写Promise
需要注意的点:
- promise实例的
**then()**方法最多接受两个参数:用于Promise兑现和拒绝情况的回调函数。onReject一个在此 Promise 对象被拒绝时异步执行的函数。它的返回值将成为catch()返回的 Promise 对象的兑现值。如果onRejected不是一个函数,则内部会被替换为一个_抛出器_函数((x) => { throw x; }),它会抛出它收到的拒绝原因。重点就是onRejected只有返回了内容,才会被catch捕捉到,没有返回内容就默认上一次promise是resolve掉了,后面的then执行的是上次onFulfilled的结果。 - 当resolve或者reject在定时器里面,那么定时器结束后再执行then
链式调用关键点
- 1、then方法本身会返回一个新的Promise对象
- 2、如果返回值是promise对象,返回值为成功,新promise就是成功
- 3、如果返回值是promise对象,返回值为失败,新promise就是失败
- 4、如果返回值非promise对象,新promise对象就是成功,值为此返回值
// 链式调用 输出200
const p3 = new Promise((resolve,reject)=>{
resolve(100)
}).then(res=>res*2,err=>console.log(err))
.then(res=>console.log(res),err=>console.log(err))
// 链式调用 输出300
const p4 = new Promise((resolve,reject)=>{
resolve(100)
// 返回值受新的promise影响,新的promise成功则成功 新的promise失败则失败
}).then(res => new Promise((resolve, reject) => resolve(3 * res)), err => console.log(err))
.then(res => console.log(res), err => console.log(err))
手写Promise代码+例子演示
class MyPromise {
// 构造方法
constructor(executor){
this.initValue()
this.initBind()
try{
executor(this.resolve,this.reject)
}catch(e){
// Promise中有throw的话,就相当于执行了reject
this.reject(e)
}
}
initBind(){
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
this.onFulfilledCallbacks = [] // 保存成功的回调
this.onRejectedCallbacks = [] // 保存失败的回调
}
initValue(){
this.PromiseResult = null
this.PromiseState = 'pending'
}
resolve(value){
if(this.PromiseState !== 'pending') return
this.PromiseState = 'fulfilled'
this.PromiseResult = value
while(this.onFulfilledCallbacks.length){
this.onFulfilledCallbacks.shift()(this.PromiseResult)
}
}
reject(reason){
if(this.PromiseState !== 'pending') return
this.PromiseState = 'rejected'
this.PromiseResult = reason
// 执行保存的失败回调
while(this.onRejectedCallbacks.length){
this.onRejectedCallbacks.shift()(this.PromiseResult)
}
}
then(onFulfilled,onRejected){
// 参数校验确保是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v
onRejected = typeof onRejected === 'function' ? onRejected : (reason)=>{throw reason}
const thenPromise = new MyPromise((resolve,reject)=>{
const resolvePromise = cb => {
// then是微任务 为了模拟这个特性,加上setTimeout
setTimeout(() => {
try{
const x = cb(this.PromiseResult)
if(x===thenPromise){
// 不能返回自身
throw new Error('不能返回自身')
}
if(x instanceof MyPromise){
x.then(resolve,reject)
}else{
resolve(x)
}
}catch(err){
reject(err)
throw new Error(err)
}
})
}
if(this.PromiseState === 'fulfilled'){
resolvePromise(onFulfilled)
}else if(this.PromiseState === 'rejected'){
resolvePromise(onRejected)
}else if(this.PromiseState === 'pending'){
// executor是异步的,不能保证延迟执行then,但是可以保证延迟执行then的回调
// 也就是说把then的回调函数缓存起来 等执行了resolve或者reject再去执行这个缓存的函数
this.onFulfilledCallbacks.push(resolvePromise.bind(this,onFulfilled))
this.onRejectedCallbacks.push(resolvePromise.bind(this,onRejected))
}
})
// 返回这个包装的promise
return thenPromise
}
}
// 输出 ”成功“
const test = new MyPromise((resolve, reject) => {
resolve('成功')
}).then(res => console.log(res), err => console.log(err))
// 只能改变一次状态
const test1 = new MyPromise((resolve, reject) => {
// 只以第一次为准
resolve('成功')
reject('失败')
})
console.log(test1) // MyPromise { PromiseState: 'fulfilled', PromiseResult: '成功' }
// 定时器输出结果
const test2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('成功') // 1秒后输出 成功
// reject('失败') // 1秒后输出 失败
}, 1000)
}).then(res => console.log(res), err => console.log(err))
// resolve reject 输出
const test3 = new Promise((resolve, reject) => {
resolve(100) // 输出 状态:成功 值: 200
// reject(100) // 输出 状态:成功 值:300
}).then(res => 2 * res, err => 3 * err)
.then(res => console.log('成功', res), err => console.log('失败', err))
// 测试链式调用then
const test4 = new Promise((resolve, reject) => {
// resolve(100) // 输出 状态:失败 值:200
reject(100) // 输出 状态:成功 值:300
}).then(res => new Promise((resolve, reject) => reject(2 * res)), err => new Promise((resolve, reject) => resolve(3 * err)))
.then(res => console.log('成功', res), err => console.log('失败', err))
// promise.then微任务测试
const test5 = new MyPromise((resolve, reject) => {
resolve(1)
}).then(res => console.log(res), err => console.log(err))
console.log(2)
promise 的 api
Promise.resolve- 制造一个成功或者失败
在一个promise.resolve()中再嵌套一层promise,并返回错误结果,那么最终结果就是失败
function test(){
return Promise.resolve(new Promise((resolve,reject)=>reject('err')))
}
test().then(e=>console.log(e))
Promise.reject- 制造一个失败
Promise.all()
并行执行所有 promise,等待全部成功,只要有一个失败,就不会继续执行其他的 promise
static all(promises) {
const result = []
let count = 0
return new MyPromise((resolve,reject)=>{
const addData = (index,value)=>{
result[index] = data
count++
if(count === promises.length) resolve(result)
}
promises.forEach((promise,index)=>{
if(promise instanceof MyPromise){
promise.then((res)=>{
addData(index,res)
},err => reject(err))
}else{
addData(index,promise)
}
})
})
}
Promise.allSettled
可以打印出所有prmomise的结果和状态
static allSettled(promises){
return new MyPromise((resolve, reject) => {
const res = []
let count = 0
const addData = (status,value,i)=>{
res[i] = {
status,
value
}
count++
if(count === promises.length){
resolve(res)
}
}
promises.forEach((promise,i)=>{
if(promise instanceof MyPromise){
promise.then((res)=>{
addData('fulfilled',res,i)
},err => addData('rejected',err,i))
}else{
addData('fulfilled',promise,i)
}
})
})
}
Promise.race
哪个请求先完成就输出哪个
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach((promise)=>{
if(promise instanceof MyPromise){
promise.then(res=>{
resolve(res)
},err=>reject(err))
}else{
resolve(promise)
}
})
})
}
Promise.any
any与all相反
- 接收一个Promise数组,数组中如有非Promise项,则此项当做成功
- 如果有一个Promise成功,则返回这个成功结果
- 如果所有Promise都失败,则报错
static any(promises) {
return new Promise((resolve,reject)=>{
let count = 0
promises.forEach((promise,i)=>{
if(promise instanceof Promise){
promise.then(val=>resolve(val),
err=>{
count++
if(count === promises.length){
reject(new AggregateError('All promises were rejected'))
}
})
}else{
resolve(promise)
}
})
})
}
Promise题目总结
实现一个promise.retry
Promise.retry接口 1、参数1:返回Promise的函数 2、参数2:重试次数 3、返回promise
Promise.retry = function(cb,times){
return new Promise((resolve,reject)=>{
function next(){
cb.then((res)=>{
resolve(times)
}).catch((e)=>{
if(times){
next()
times--
}else{
reject(e)
}
})
}
next()
})
}
promise多请求并发控制
import axios from "axios"
const requestQueue = (concurrency = 6)=>{
const queue = [];
let current = 0;
const dequeue = () => {
while(current < concurrency && queue.length){
current ++;
const temp = queue.shift()
temp.then((res)=>{}).catch((e)=>{})
.finally(()=>{
current--;
dequeue()
})
}
}
return (resF)=>{
queue.push(resF)
dequeue()
}
}
const enqueue = requestQueue()
for(let i = 0; i<req.length;i++){
enqueue(()=>axios.get('/api/test'+i))
}
如下的promise.all会输出什么
- 首先同步输出 start 0 start 1000 start 2000 start 3000 start 4000,因为primise之外的可以同步执行
- 其次间隔指定时间输出 end 0 end 1000 end 2000 end 3000 end 4000
- 最后输出一个数组 [0, 1000, 2000, 3000, 4000]
// createTask有两层函数
function createTask(ms) {
return () => {
// primise之外的可以同步执行
console.log("start", ms);
// promise之内的是异步执行
return new Promise(r =>
setTimeout(() => {
console.log("end", ms);
r(ms);
}, ms)
);
};
}
// 此处相当于调用createTask外部的函数
const tasks = Array(5)
.fill(null)
.map((_, i) => createTask(i * 1000));
// 此处相当于调用createTask外=内部的函数
// promise.all是需要等所有的promise执行完毕才能进行then
Promise.all(tasks.map(task => task())).then(res => {
console.log(res);
});
实现一个limitTask函数,同时只能有2个任务进行
核心就是递归,promise.all,promise.then
limitRunTask(tasks, 2).then(console.log);
function limitRunTask(alltasks, limitNumber) {
let loop = 0
let resArr = []
function run(tasks) {
if (!tasks.length) return Promise.resolve(resArr)
return Promise.all(tasks.map(() => tasks()))
.then((res) => {
resArr.push(...res)
console.log(loop, '任务结束')
loop++
return run(
alltasks.slice(loop * limitNumber, loop * limitNumber + limitNumber)
)
})
}
run(alltasks.slice(loop * limitNumber, loop * limitNumber + limitNumber))
}
实现一个 Queue类,按照指定间隔执行函数,当start函数被调用的时候才开始
实现一个Queue类,要求包含两个函数 task函数:新增一个任务。包含两个参数,等待时间和回调函数 ; start函数:执行任务队列。将所有任务按队列顺序执行,执行完一个任务才能执行下一个任务 ps:下面代码,实现添加3个任务,然后执行3个任务。隔1秒,打印1;再隔2秒,打印2;再隔1秒,打印3 其实也是一个递归,不过限制条件变成1了,因为要一个一个的去执行
class Queue {
constructor(){
this.allTasks = [];
this.limitNumber = 1;
this.loop = 0;
}
task(wait,cb){
this.allTasks.push({wait,cb});
// 因为此处是链式调用 所以要返回this
return this
}
start(){
// 启动任务
return this.run(
this.allTasks.slice(
this.loop*this.limitNumber, this.loop*this.limitNumber + this.limitNumber
)
)
}
run(tasks){
const detail = tasks[0];
if(!detail){
this.loop = 0;
return Promise.resolve()
}
return new Promise((resolve,reject)=>{
setTimeout(()=>{
detail.cb()
this.loop++;
resolve()
},detail.wait)
}).then(()=>{
this.run(
this.allTasks.slice(
this.loop*this.limitNumber, this.loop*this.limitNumber + this.limitNumber
)
)
})
}
}
new Queue()
.task(1000,()=>{console.log(1)})
.task(2000,()=>{console.log(2)})
.task(3000,()=>{console.log(3)})
.start()
参考文章