前端React项目处理并发的最佳实践

什么是并发?

因为js是单线程的,所以前端的并发指的是在极短时间内发送多个数据请求,比如说循环中发送ajax。

举一个简单的例子:

下面一段代码是常规的mount阶段执行的请求:

useEffect(async () => {
    console.time();
    await TaskBizService.querySpyTaskSummary();
    await TaskBizService.querySpyTask();
    console.timeEnd();
    
    // time: 300ms
}, [])

更换成这样:

useEffect(() => {
    console.time();
    Promise.all([
        TaskBizService.querySpyTaskSummary(),
        TaskBizService.querySpyTask(),
    ]).then((res) => {
        console.timeEnd();
    });

    // time: 120ms
}, [])

可以看出有很大的性能优化空间和区别,如果在页面渲染时,多个请求没有相互数据依赖性(依赖请求),直接采用并行请求会加快页面中数据展现的时间,这两个demo同时也涉及到事件循环的一些知识。

因此也可以在页面中找到一些可优化的请求,转换为并行,对于首屏渲染的优化是有很大帮助的。

Promise.all

可以采用Promise.all处理并发, 当所有promise全部成功时, 会走.then,并且可以拿到所有promise中传进resolve中的值。

看一下这段代码:

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	resolve('request2 end')
  }, 3000);
})

console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {
  console.timeEnd(); // default: 3.2s
  console.log(result); // (2) ['request1 end', 'request2 end']  
})

如果Promise.all中有实例失败,整个并发会全部挂掉。

就像这段代码:

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	reject('request2 fail')
  }, 3000);
})

console.time(); // 开始计时
Promise.all([p1, p2])
.then(result => {
  // 不会走到这一步
  console.timeEnd(); // default: 3.2s
  console.log(result); 
})

总结

  1. Promise.all在处理并发时,如果有一个promise失败,就不会走.then, 但是如果只是需要在所有异步执行完成之后去执行其它副作用代码,可以把代码写在.finally中实现。
  2. 但是如果需要在有异步失败之后,获取到promise实例通过resolve传过来的值,则不行。

Promise.allSettled

Promise.all中实现不了的功能, 可以使用Promise.allSettled来实现, 在Promise.allSettled中,当其中有一个promise执行失败时,会继续走.then, 并且可以同时拿到传给resolvereject的值,可以通过回调值来判断接口的请求结果来做二次处理。

代码改造起来也非常简单,如下:

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	resolve('request1 end')
  }, 1000);
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
  	resolve('request2 end')
  }, 3000);
})

console.time(); // 开始计时
Promise.allSettled([p1, p2])
.then(result => {
  console.timeEnd(); // default: 3.2s
  console.log(result); // (2) ['request1 end', 'request2 end']  
})

当请求结果全部resolve的情况,Promise.allSettledPromise.all没有太大区别,只是对于catch的情况下做了一次弥补。同时Promise.allSettled的兼容性并没有Promise.all来得好。

总结

  1. Promise.allSettled在处理并发时,不怕异步执行失败,继续会走.then,完美解决Promise.all在并发有失败情况下,拿不到值的情况
  2. 唯一的缺点,比起Promise.all兼容性差一点,多了两个不支持的浏览器, 安卓端火狐浏览器(Firefox for Android)及 Samsung Internet 浏览器。
     

async/await

async/await能处理并发吗?答案是可以的,但是代码会看起来臃肿不易读一下,先看一下常规的async/await的异步处理方案吧。

代码如下:

let getData1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request1 end')
    }, 2000);
  })
};

let getData2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request2 end')
    }, 3000);
  })
}
let syncFn = async () => {
    console.time(); // 开始计时
    
    const data1 = await getData1();
    const data2 = await getData2();
    
    console.timeEnd(); // default: 5.8s
    console.log(data1, data2); // request1 end, request2 end
}

syncFn();

从耗时可以看到,async/await造成了异步阻塞,实际走的是串行(依赖)请求,也是普通项目中最常见的处理方式,接下来我们改造一下async/await并行请求的方案。

let getData1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request1 end')
    }, 2000);
  })
};

let getData2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('request2 end')
    }, 3000);
  })
}
let syncFn = async () => {
    console.time(); // 开始计时
    
    const p1 = getData1();
    const p2 = getData2();

    const data1 = await p1;
    const data2 = await p2;
    
    console.timeEnd(); // default: 3.8s
    console.log(data1, data2); // request1 end, request2 end
}

syncFn();

总结

  1. 如果是在安装了axios或者其它请求库,在这些库本身提供支持并发的api情况下,如axios.all、axios.spread,建议使用他们提供的这些api,因为作为一个百万级下载量的库,提供的这些api考虑到的边界条件、兼容性肯定是比其它原生方法好用的。
  2. 在没有这些库的情况下,推荐使用Promise.allSettled,如果你喜欢用 async、await 也可以,结果没有区别,看个人喜好了。但是在我看来在处理并发的情况下 async、await的写法感觉就不那么简洁了。
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React前端电商项目是一种使用React框架搭建的电商网站前端项目。它基于React的组件化思想,将页面拆分成多个可复用的组件,使开发更加高效和模块化。 在React前端电商项目中,常见的功能包括用户登录注册、商品展示、购物车、下单结算等。用户可以通过注册和登录功能,创建自己的账号,方便进行商品购买和订单管理。 商品展示是项目的核心部分,通过React的虚拟DOM技术,可以实现页面的快速渲染和实时更新。同时,通过组件化的思想,可以将商品信息、图片、价格等数据动态地渲染到页面上,提供给用户直观的浏览和选择。 购物车是电商项目中的重要功能,用户可以将自己喜欢的商品加入购物车,通过React的状态管理机制,可以实现购物车数量的实时更新和显示。用户可以对购物车中的商品进行增删改查等操作,方便进行商品管理和订单结算。 在下单结算部分,用户可以选择需要购买的商品和数量,并进行价格计算、收货地址选择等操作。通过React的表单处理技术,可以方便地获取用户输入的信息,并进行验证和提交操作。 总结来说,React前端电商项目具有模块化、高效、灵活等特点,能够为用户提供良好的购物体验。它使用React框架进行开发,借助其组件化和状态管理等特性,可以快速构建出功能完善的电商网站前端

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值