Promise基础
Promise.race 接受可迭代的对象,把状态设置为第一个 resolve 或 reject 的对象的状态,也就是返回的对象的状态由最快变化的 promise 对象决定,所以称为 race
但是注意,即便 race 返回的状态已经确定,其他的 promise 也会执行,并不是被吞掉了,利用这一点,可以帮助我们完成异步控制并发
async/await
已经不算新语法的两兄弟,首先 await 只能在 async 中使用,如果没有适用环境的话,可以用立即执行函数与其配合
await 后面的语句相当于 then 的回调,所以看起来是阻塞代码的,其实整体任务仍然是异步执行的,只是看起来很像同步代码
async 返回 promise 对象,如果返回值不为 promise,会调用 Promise.resolve 将其封装为 promise 对象再返回
比如说
const request=(time)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{resolve(time)},time)
})
}
async function async1(){
const p1=await request(1000)
console.log(p1)
const p2=await request(2000)
console.log(p2)
const p3=await request(1000)
console.log(p3)
}
async1()
会按照秒数依次打印,看起来就像同步代码,而不是像 then 一样,需要分析执行顺序
异步控制并发
我们先思考一下,首先需要一个池子,来盛放并观察任务的数量,等数量达到限制时,我们需要执行任务,来释放空间,同时还需要加入任务,来控制后面任务的处理,我们先来试一下吧
const request=(time)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{resolve(time)},time)
})
}
const asyncPool=(tasks,max)=>{
const pool=[]
for(let i=0;i<tasks.length;i++){
const task=request(1000)
pool.push(task)
task.then(res=>{
console.log(res)
pool.splice(pool.indexOf(task),1)
})
}
}
const tasks = new Array(10).fill(0).map((v,i)=>i)
asyncPool(tasks,3)
很显然我们犯了异步编程的最简单的错误,for 循环在短时间内就能完成,效果就是 10 个 task 同时等待 then 中的回调,那么就会发生以上结果
所以我们必须阻塞一下代码的执行,我们首先可以使用 await 来做,那么什么时候阻塞呢,很容易想到当任务池子,也就是 pool 长度达到 max 时,我们需要执行异步任务,并释放空间,以便后续代码执行
修改如下
const request=(i)=>{
return new Promise((resolve,reject)=>{
setTimeout(()=>{resolve(i)},1000)
})
}
const asyncPool=async(tasks,max)=>{
const pool=[]
for(let i=0;i<tasks.length;i++){
const task=request(i)
pool.push(task)
task.then(res=>{
console.log(res,`当前并发数为${pool.length}`)
pool.splice(pool.indexOf(task),1)
})
if(pool.length===max){
await Promise.race(pool)
}
}
}
const tasks = new Array(10).fill(0).map((v,i)=>i)
asyncPool(tasks,3)
首先要加上 async
其次我们使用 Promsie.race 来释放任务池,从头开始分析,一直到 i 为 2 时,也就是 pool 里面已经存入了 3 个任务时,这时符合判断条件,用 await 去等待这三个任务,当然是第一个任务先 resolve,但是不代表其他两个任务也不执行
重要的是,只要第一个任务 resolve,也就是 Promise.race 返回的 promise 的状态变为 fullfilled,await 就会执行后续代码,也就是继续 for 循环,继续重复上述过程