FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
362 stars 39 forks source link

请求串行控制和并行控制 #175

Open FrankKai opened 4 years ago

FrankKai commented 4 years ago

实际项目中遇到的串行并行控制问题

最近也遇到了一个并发请求导致服务端插入重复数据报错的坑,是因为前端并发了多个相同的Create请求,服务端多线程提交数据库事务存在冲突,所以导致报错。但是由于前端发送重复请求是相对较好的处理方式,所以最终改成了阻塞请求。 具体的前端实现方式,异步并发:Promise.all;同步阻塞:await。Promise.all异步并发与await同步阻塞。

并发式的Promise.all(导致后端服务出问题)

async promiseAllAsyncConcurrence(items) {
  const promiseArr = [];
  for (let i = 0; i < 9; i++) {
    promiseArr[i] = new Promise((resolve)=>{
      setTimeout(()=>{
        resolve(items[i]);
      }, Math.random()*1000)
    })
  }
  return Promise.all(promiseArr);
}
const sourceArr = ['foo', 'bar', 'baz']
const resultArr = await promiseAllAsyncConcurrence(sourceArr);

阻塞式的await(后端服务正常运转)

awaitSequenceSync(item) {
  return new Promise((resolve)=>{
    setTimeout(()=>{
      resolve(items);
    }, Math.random()*1000)
  })
}
const sourceArr = ['foo', 'bar', 'baz']
const resultArr = [];
for (let i = 0; i < 9; i++) {
  // eslint-disable-next-line
  resultArr[i] = await awaitSequenceSync(sourceArr[i]);
}

注意:如果不添加会报ESLint error: Unexpected await inside a loop no-await-in-loop

并发的Promise.all添加setTimeout定时器(不推荐)

async promiseAllAsyncConcurrence(items) {
  const promiseArr = [];
  for (let i = 0; i < 9; i++) {
    promiseArr[i] =  new Promise((resolve)=>{
        setTimeout(()=>{
            new Promise((resolve)=>{
              setTimeout(()=>{
                resolve(items[i]);
              }, Math.random()*1000)
            })
        }, (i+1)*500) // 每个请求间隔500ms
    })
  }
  return Promise.all(promiseArr);
}
const sourceArr = ['foo', 'bar', 'baz']
const resultArr = await promiseAllAsyncConcurrence(sourceArr);

如何使用async,await实现串控制

/**
 * 串行控制:一个接着一个发请求
 * @param {*} items
 * @param {*} asyncFunc
 */

function seriesFlow(items, asyncFunc) {
  const result = [];
  items.forEach(async (item, i) => {
    result[i] = await asyncFunc(item);
  });
  return result;
}

如何使用promise.all实现并行控制

/**
 * 并行控制:请求一次性并行发出
 * @param {*} items
 * @param {*} asyncFunc
 */

function parallelFlow(items, asyncFunc) {
  return new Promise((resolve, reject) => {
    const promises = [];
    items.forEach((item, i) => {
      promises[i] = asyncFunc(item);
    });
    return Promise.all(promises)
      .then(data => {
        resolve(data);
      })
      .catch(err => {
        reject(err);
      });
  });
}

使用示例可以到我的个人npm ujf查看:https://www.npmjs.com/package/ujf 上面源码位置:https://github.com/FrankKai/UJF/blob/master/src/task.js

如何使用第三方库Nimble实现串并行控制

Nimble串行控制

Series会在之前的一个函数完成后再执行下一个函数,最后一个函数执行完毕时进入最终回调。

_.series([
    function (callback) {
        setTimeout(function () {
            console.log('one');
            callback();
        }, 25);
    },
    function (callback) {
        setTimeout(function () {
            console.log('two');
            callback();
        }, 0);
    }
], ()=>{
    // do something...
});

Nimble并行控制

Parallel会同时执行所有函数,当所有函数执行完毕时进入最终回调。

_.parallel([
    function (callback) {
        setTimeout(function () {
            console.log('one');
            callback();
        }, 25);
    },
    function (callback) {
        setTimeout(function () {
            console.log('two');
            callback();
        }, 0);
    }
], ()=>{
    // do something...
});

参考资料http://caolan.github.io/nimble/

如何使用第三方库blueBird实现串并行控制

Promise.mapSeries实现串行控制

参考资料:http://bluebirdjs.com/docs/api/promise.mapseries.html

Promise.all实现并行控制

参考资料:http://bluebirdjs.com/docs/api/promise.all.html