Node.js 异步编程之 Callback介绍(一)
Node.js基于JavaScript引擎v8,是单线程的。Node.js采用了与通常Web上的JavaScript异步编程的方式来处理会造成阻塞的I/O操作。在Node.js中读取文件、访问数据库、网络请求等等都有可能是异步的。对于Node.js新人或者从其他语言背景迁移到Node.js上的开发者来说,异步编程是比较痛苦的一部分。本章将由浅入深为大家讲解Node.js异步编程的方方面面。从最基础的callback到thunk、Promise、co直到ES7计划的async/await。
首先我们先从一个具体的异步编程的例子说起。
获取多个ip所在地的天气信息
在ip.json这个文件中,有一个数组我们存放了若干个ip地址,分别来自不同的地方的不同访问者,内容如下:
//ip.json ["115.29.230.208","180.153.132.38","74.125.235.224","91.239.201.98","60.28.215.115"]
希望可以每一个ip所在地当前的天气。将结果输出到weather.json这个文件中各式如下:
//weather.json
[
{"ip":"115.29.230.208","weather":"Clouds","region":"Zhejiang"},
{"ip":"180.153.132.38","weather":"Clear","region":"Shanghai"},
{"ip":"74.125.235.224","weather":"Rain","region":"California"},
{"ip":"60.28.215.115","weather":"Clear","region":"Tianjin"}
]
整理思路,我们分成以下几步来完成:
1.读取ip地址;
2.根据ip地址获取ip所在地的地理位置;
3.根据地理位置查询当地的天气;
4.将结果写入到weather.json文件中。
这些步骤都是异步的(读写文件可以同步,但作为示例,都用异步)。
callback
首先我们尝试不借助任何库,试着以Node.jsAPI通常提供的方式——专递一个callback作为异步回调——来实现。我们将借助三个基础模块:
1.fs:从文件ip.json读取IP列表;把结果写入到文件中;
2.request:用来发送HTTP请求,根据IP地址获取geo数据,再通过geo数据获取天气数据;
3.querystring:用来组装发送请求的url参数。
新建一个callback.js文件,引入这几个模块:
//callback.js
varfs=require('fs')
varrequest=require('request')
varqs=require('querystring')
读取文件中的IP列表,调用fs.readFile读取文件内容,再通过JSON.parse来解析JSON数据:
...
functionreadIP(path,callback){
fs.readFile(path,function(err,data){
if(err){
callback(err)
}else{
try{
data=JSON.parse(data)
callback(null,data)
}catch(error){
callback(error)
}
}
})
}
...
接着就是使用IP来获取geo,我们使用request来请求一个开放的geo服务:
...
functionip2geo(ip,callback){
varurl='http://www.telize.com/geoip/'+ip
request({
url:url,
json:true
},function(err,resp,body){
callback(err,body)
})
}
...
使用geo数据来获取weather:
...
functiongeo2weather(lat,lon,callback){
varparams={
lat:lat,
lon:lon,
APPID:'9bf4d2b07c7ddeb780c5b32e636c679d'
}
varurl='http://api.openweathermap.org/data/2.5/weather?'+qs.stringify(params)
request({
url:url,
json:true,
},function(err,resp,body){
callback(err,body)
})
}
...
现在我们已经获取geo、获取weather的接口,接下来我们还有稍微复杂的问题要处理,因为ip有多个,所以我们需要并行地去读取geo已经并行地读取weather数据:
...
functionips2geos(ips,callback){
vargeos=[]
varip
varremain=ips.length
for(vari=0;i<ips.length;i++){
ip=ips[i];
(function(ip){
ip2geo(ip,function(err,geo){
if(err){
callback(err)
}else{
geo.ip=ip
geos.push(geo)
remain--
}
if(remain==0){
callback(null,geos)
}
})
})(ip)
}
}
functiongeos2weathers(geos,callback){ varweathers=[] vargeo varremain=geos.length for(vari=0;i<geos.length;i++){ geo=geos[i]; (function(geo){ geo2weather(geo.latitude,geo.longitude,function(err,weather){ if(err){ callback(err) }else{ weather.geo=geo weathers.push(weather) remain-- } if(remain==0){ callback(null,weathers) } }) })(geo) } } ...