我实现的Node.js中类似Sleep的功能,请大家点评!
发布于 9 年前 作者 i2Worker 16303 次浏览 最后一次编辑是 8 年前 来自 分享

最近在抓取一个网站的数据,它有反爬虫策略,不让我持续访问超过100个网络链接,如果访问一段时间暂停一会的话就可以绕过去了,所以就有了下面这段代码,请大家看看,有没有更好的实现方法,首先说明一下,这个小程序运行在命令行下就可以了,我只要抓取数据就可以了。 实现的思路: 主要是通过setTimeout来实现暂停功能,将要抓取的连接保存在一个数组中,将数组进行分组,每抓取完3个连接就暂停5秒钟,并且结果统一保存在一个外部变量results中。

//测试Nodejs怎样实现Sleep的功能

var async = require('async');

//通过setTimeout函数实现Sleep的功能
var sleep = function(array, callback) {
  'use strict';

  //初始化变量
  var results = [];  //存放最终结果
  var tmpArray = array.slice(0);  //拷贝一份数据,防止改变变量外部的值
  var isEnd = false;

  //自调用函数
  var run = function() {
    var tmp = null; //存放截取的数组
    if (tmpArray.length > 3) {
      tmp = tmpArray.splice(0, 3);
    } else {
      tmp = tmpArray;
      isEnd = true;
    }
    async.mapLimit(tmp, 2, function(item, itemCallback) {
      console.log('Enter: ' + item.name);
      setTimeout(function() {
        console.log('Handle: ' + item.name);
        itemCallback(false, item.name + '!!!');
      }, item.delay);
    }, function(err, data) {
      console.log(data);
      results = results.concat(data);
      if (isEnd) {
        callback(false, results);
      } else {
        console.log('Sleep 5s');
        setTimeout(run, 5000);
      }
    });
  };

  run();
};

//测试数据
var arr = [
  {name:'Jack', delay:2000},
  {name:'Mike', delay: 1000},
  {name:'Freewind', delay:3000},
  {name:'Test', delay: 2500},
  {name:'Woody', delay: 3500},
  {name:'Water', delay: 2600},
  {name:'Sugar', delay: 1500}
];

sleep(arr, function(error, data) {
  'use strict';

  console.log(data);
});
9 回复

有 setTimeout 还非要去实现一个 sleep, 很强烈的 Java/C# 即视感.

@TossShinHwa 这个本来就是用的setTimeout功能,只是用它来实现暂停而已。

表示无法理解楼主心态

其实就是递归转迭代??

let arr = [
  {name:'Jack'},
  {name:'Mike'},
  {name:'Freewind'},
  {name:'Test'},
  {name:'Woody'},
  {name:'Water'},
  {name:'Sugar'}
]

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

function download(name) {
  return new Promise(resolve => setTimeout(
    () => resolve('result for ' + name),
    Math.floor(Math.random() * 1000)
  ))
}

(async function main() {
  const concurrency = 3;
  for (let i = 0; i < arr.length; i+=concurrency) {
    console.log(i, '----')
    let task = [];
    for (let j = 0; j < concurrency && i +j < arr.length; j++) {
      task.push(download(arr[i+j].name));
    }
    var result = await Promise.all(task)
    console.log(result)
    await sleep(5000)
  }
})()

@qingfeng 好精巧的sleep函数。我在这里学习了。谢谢!!

就是用 settimeout 实现了自己的等待逻辑而已。没什么好点评的

var arr = [
  {name:'Jack', delay:2000},
  {name:'Mike', delay: 1000},
  {name:'Freewind', delay:3000},
  {name:'Test', delay: 2500},
  {name:'Woody', delay: 3500},
  {name:'Water', delay: 2600},
  {name:'Sugar', delay: 1500}
];

function sleep(array, callback) {
  function next() {
    var data = array.shift();
    if (data) {
      setTimeout(next, data.delay);
      callback(null, data.name);
    }
  }
  next();
}

sleep(arr, function (error, data) {
  console.log(data);
});

你这样写是递归,可以 setInterval 周期执行run不需要递归;递归的开销大 就是需要将请求的链接资源放在run外面, 和result一个作用域

另外run里面用的 var建议换成let 或者const 否则他们的作用域基本都和result差不多

回到顶部