Javascript中,

  • 匿名函数多用于实现回调函数和闭包
  • 闭包=函数+引用环境,
  • promiseES6中语言标准,保存着某个未来才会结束的事件(通常是一个异步操作)的结果.
  • 想看他们都是如何使用,请阅读本文,或许您会有一种恍然大明白的感觉。
const promise = new Promise(function(resolve, reject) {
    // ... some code

    if ( /* 异步操作成功 */ ) {
        resolve(value);
    } else {
        reject(error);
    }
});

0. 引言

工作中,有一个业务功能:周期扫描任务,每一个周期会扫描出数据,然后按照周期数存到 elasticsearch , 现在需要每个周期之间的数据是否有重叠关联的数据去支撑周期快照功能,通过 Query DSL +kibana 可视化查询,或可通过肉眼比对,未免劳心劳力。 kibana 也是通过 http post 请求,然后返回数据 json ,那么我们完全可以模拟 kibana 请求,获取数据,再通过代码比对相邻周期数据,输出文件,一个小爬虫兼数据分析的小工具构想浮现在脑海中。

1. 第一个index.js

由于真实代码在公司,示例代码做了修改, url 替换成了百度 , post 替换为 get

const http = require('https');

function spider() {
    for (i = 0; i < 9; i++) {
        //i为某个周期参数
        http.get('https://www.baidu.com/', function(res) {
            console.log(`当前i:${i}`);
            console.log(`状态码${res.statusCode}`);
        })
    }
}

spider()

node .\src\server\index.js

当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200
当前i:9
状态码200

这是为什么?因为 http.get 是异步方法,并不会等待,会继续执行循环, i 值随即也会发生变化,而这时异步方法中对 i 的引用也就变成了9。我们肯定想输出的是不同的 i 值,才能看对应周期的数据。怎么办?匿名函数

2. 第二个index.js

const http = require('https');

function spider() {
    for (i = 0; i < 9; i++) {
        //闭包
        //匿名函数+立即执行
        (function(i) {
            http.get('https://www.baidu.com/', function(res) {
                console.log(`当前i:${i}`);
                console.log(`状态码${res.statusCode}`);
            })
        })(i)
    }
}

spider()

node .\src\server\index.js

当前i:1
状态码200
当前i:6
状态码200
当前i:5
状态码200
当前i:2
状态码200
当前i:4
状态码200
当前i:3
状态码200
当前i:0
状态码200
当前i:8
状态码200
当前i:7
状态码200

闭包=函数+引用环境,函数就是匿名函数,引用环境则是传参 i

3. 第三个index.js

如果需求就是查看周期:周期数据,这种 key:value 的需求,那么上面基本已经满足需求了,如果觉得匿名函数+立即执行不好理解,改造如下,也好理解。

const http = require('https');

//let map=new Map();

function getData(i) {
    http.get('https://www.baidu.com/', function(res) {
        console.log(`当前i:${i}`);
        console.log(res.statusCode);
        //map.set ...
    })
}

function spider() {
    for (i = 0; i < 9; i++) {
        getData(i)
    }
}

spider()

但是现在,我们需要把每个周期与周期数据都存起来,然后做数据分析。换言之,我们需要循环创建的多个http异步请求,全部执行完,且返回数据,并存起来,才能做分析。别忘了这是异步,定义全局变量 let map=new Map(); 和在异步回调中 map.set 这种是行不通的。这时就是 Promise 登场的时候。

4. 第四个index.js

const http = require('https');

//定义一个数组
let array = Array();

//定义一个数组
let promiseArray = []

//定义一个map
let map = new Map();

function getData(i) {
    return new Promise((resolve, reject) => {
        http.get('https://www.baidu.com/', function(res) {
            console.log(`当前i:${i}`);
            console.log(res.statusCode);

            array.push(i);
            map.set(i, res.statusCode);

            resolve();
        })
    })
}

function spider() {
    console.log('开始循环创建promiseArray');
    for (i = 0; i < 9; i++) {
        promiseArray.push(getData(i))
    }
    console.log('结束循环创建promiseArray');
}

spider();

//Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中  promise 有一个失败(rejected),此实例回调失败(reject),失败原因的是第一个失败 promise 的结果。
Promise.all(promiseArray).then(function() {
    console.log(array.length);
    console.log(array);
    console.log(map);
})

每一个异步请求都创建一个 Promise 对象, 并装进一个存放 Promise 对象的数组,然后调用 Promise.all ,还是返回一个 Promise 对象,他的回调完成是 Promise 对象数组中的每一个都 resolve ,即所有的异步请求都完成了.

开始循环创建promiseArray
结束循环创建promiseArray
当前i:7
200
当前i:4
200
当前i:1
200
当前i:2
200
当前i:6
200
当前i:5
200
当前i:8
200
当前i:3
200
当前i:0
200
9
[
  7, 4, 1, 2, 6,
  5, 8, 3, 0
]
Map {
  7 => 200,
  4 => 200,
  1 => 200,
  2 => 200,
  6 => 200,
  5 => 200,
  8 => 200,
  3 => 200,
  0 => 200
}