在node.js中,如果用yield控制并发操作

发布于 2022-09-06 09:40:17 字数 1552 浏览 24 评论 0

问题背景:

我用的是egg1.x,基于koa。然后就是各种generator,各种yield调用。这样的好处是编码舒服,都是同步代码。缺点是在某些情况下,速度会异常缓慢。比如有些逻辑需要基于http协议循环调用另外的服务。egg官网上提供了一种基于yield的控制并发方法,代码如下:

const [ news, user ] = yield [
  ctx.service.news.list(topic),
  ctx.service.user.get(uid),
];

这是一种极度简单的使用场景。它无法控制并发量。所以我把它改造成了如下代码:

      const concurrency = 5;
      let index = 0;
      let tempArr = [];
      for (const item of data) {
        tempArr.push(ctx.service.user.get(item.uid));
        if (index === concurrency) {
          yield tempArr;
          tempArr = [];
          index = 0;
        }
        index++;
      }
      if (tempArr.length) {
        yield tempArr;
      }

这样写确实可以达到效果,当然如果需要处理返回值,会更复杂一点。但是本人水平有限,想不出什么办法将这个方法进行封装。所以想问的第一个问题便是如何封装它

async的maplimit

在async的文档上找到如下代码

async.mapLimit(urls, 5, async function(url) {
    const response = await fetch(url)
    return response.body
}, (err, results) => {
    if (err) throw err
    // results is now an array of the response bodies
    console.log(results)
})

这不就是我梦寐以求的吗?然而无情的现实是公司目前并不想升级nodejs,所以还是得乖乖用generator。后来我又想把上面的代码改造成generator/yield

async.mapLimit(urls, 5, function* (url) {
    const response = yield fetch(url)
    return response.body
}, (err, results) => {
    if (err) throw err
    // results is now an array of the response bodies
    console.log(results)
})

然而一个回调函数都执行不了。所以第二个问题是为啥不能直接改造呢,async不是generator的语法糖吗?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

幽梦紫曦~ 2022-09-13 09:40:17

第一个问题:
co-parallel

第二个问题:
你需要把 function*(url){} 用 co 包一下。generator 并不会自动执行,某种程度上你可以把 async 函数视为自动帮你 co 一下了的 generator。
但是不确定你这么写能不能执行,待会测试一下。

墨离汐 2022-09-13 09:40:17
const co = require('co');
function mapLimit(urls, limit, genFn, cb) {
  co(function* () {
    let results = [];
    while(urls.length) {
      let res = yield urls.splice(0, limit).map(genFn);
      results.push(...res);
    }
    return results;
  }).then(function(res) {
    cb(null, res)
  }).catch(cb)
}

这样封装楼主看看能吗
async.mapLimit官方文档里要求传asyncFUnction函数,传Generator肯定不行了

最终幸福 2022-09-13 09:40:17

async是generator的语法糖,但不是简单的替换就可以解决的
使用await语法自带执行器,而generator则是不带执行器,需要自己手工添加
await需要后面的变量是一个promise对象

给你个generator+promise对 async/await的实现

//一个generator执行器的实现
function run(generator){
    var gen=generator();
    var resultValue=gen.next();
    if(resultValue.done){
        return resultValue.value;
    }

    function runGenerator(value){
        Promise.resolve(value).then(function(data){
            var resultValue=gen.next(data);
            if(!resultValue.done){
                runGenerator(resultValue.value);
            }
        }).catch(function(error){
            gen.throw(new Error(error));
        });
    }

    return runGenerator(resultValue.value);
}

//执行执行器,
//传入一个generator函数,yield后面带一个promise对象
run(function *(){
    var task1=function(){
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("1000");
                resolve("1000");
            },1000);
        });
    };

    var task2=function(data){
        return new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("2000 :: "+data+"");
                resolve("2000 :: "+data+"");
            },1000);
        });
    };

    try{

        var data=yield task1();
        var data2=yield task2(data);

        return data2;
    }catch(error){
        console.log("~~~~error~~~");
    }

});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文