Node.js is single-threaded based on the JavaScript engine V8. Node.js uses the usual asynchronous programming with JavaScript on the Web to handle blocking I/O operations. It may be asynchronous to read files, Access databases, network requests, and so on in Node.js. Asynchronous programming is a painful part of node.js newcomers, or developers migrating from other language backgrounds to node.js. This chapter will explain to you all aspects of node.js asynchronous programming. From the most basic callback to Thunk, Promise, CO until ES7 plan async/await.
First, let's start with a specific example of asynchronous programming.
Get weather information for multiple IP locations
In Ip.json This file, there is an array we have a number of IP addresses, from different places of different visitors, the contents are as follows:
Copy Code code as follows:
Ip.json
["115.29.230.208", "180.153.132.38", "74.125.235.224", "91.239.201.98", "60.28.215.115"]
Want to be able to each IP location of the current weather. Output the results to weather.json this file is as follows:
Copy Code code as follows:
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"}
]
Sorting ideas, we are divided into the following steps to complete:
1. Read IP address;
2. To obtain the location of IP location based on IP address;
3. Check the local weather according to the geographical location;
4. Write the results to the Weather.json file.
These steps are asynchronous (read-write files can be synchronized, but as an example, they are asynchronous).
Callback
First we try not to use any library, try to do it in a way that the Node.js API usually provides--a callback as an asynchronous callback. We will leverage three basic modules:
1.FS: Read IP list from file Ip.json, write the result to file;
2.request: Used to send HTTP requests, obtain GEO data based on IP address, and then obtain weather data through GEO data;
3.querystring: The URL parameter used to assemble the send request.
Create a new Callback.js file and introduce these modules:
Copy Code code as follows:
Callback.js
var fs = require (' FS ')
var request = require (' request ')
var qs = require (' querystring ')
Read the IP list in the file, call Fs.readfile to read the file contents, and then parse the JSON data by Json.parse:
Copy Code code as follows:
...
function Readip (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)
}
}
})
}
...
Then we use IP to get geo, and we use request to ask for an open GEO service:
Copy Code code as follows:
...
function Ip2geo (IP, callback) {
var url = ' http://www.telize.com/geoip/' + IP
Request ({
Url:url,
Json:true
}, function (err, resp, body) {
Callback (err, body)
})
}
...
Use GEO data to get weather:
Copy Code code as follows:
...
function Geo2weather (lat, lon, callback) {
var params = {
Lat:lat,
Lon:lon,
APPID: ' 9bf4d2b07c7ddeb780c5b32e636c679d '
}
var url = ' Http://api.openweathermap.org/data/2.5/weather? ' + qs.stringify (params)
Request ({
Url:url,
Json:true,
}, function (err, resp, body) {
Callback (err, body)
})
}
...
Now that we have the GEO, get the weather interface, we have a slightly more complicated problem to deal with, because there are multiple IP, so we need to read the GEO in parallel to read weather data in parallel:
Copy Code code as follows:
...
function Ips2geos (IPs, callback) {
var geos = []
var IP
var remain = Ips.length
for (var i = 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)
}
}
function Geos2weathers (GEOS, callback) {
var weathers = []
var Geo
var remain = Geos.length
for (var i = 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)
}
}
...
Both Ips2geos and geos2weathers use a relatively primitive method, remain to calculate the number of waits to return, remain 0 means that the parallel request ends and the processing result is loaded into an array to return.
Finally, the results are written to the Weather.json file:
Copy Code code as follows:
...
function Writeweather (weathers, callback) {
var output = []
var weather
for (var i = 0; i < weathers.length; i++) {
Weather = Weathers[i]
Output.push ({
Ip:weather.geo.ip,
Weather:weather.weather[0].main,
Region:weather.geo.region
})
}
Fs.writefile ('./weather.json ', json.stringify (output, NULL, '), callback)
}
...
By combining these functions, we can achieve our goal:
Copy Code code as follows:
...
function Handlererror (err) {
Console.log (' ERROR: ' + err)
}
Readip ('./ip.json ', function (err, IPS) {
if (err) {
Handlererror (ERR)
} else {
Ips2geos (IPs, function (err, GEOS) {
if (err) {
Handlererror (ERR)
} else {
Geos2weathers (GEOS, function (err, weathers) {
if (err) {
Handlererror (ERR)
} else {
Writeweather (weathers, function (err) {
if (err) {
Handlererror (ERR)
} else {
Console.log (' success! ')
}
})
}
})
}
})
}
})
Haha, your mom nesting, you might think that's the problem with JavaScript asynchrony, and really, nesting is not the real problem with JavaScript asynchrony. The above code we can write as follows:
Copy Code code as follows:
...
function Readipcallback (err, IPs) {
if (err) {
Handlererror (ERR)
} else {
Ips2geos (IPs, Ips2geoscallback)
}
}
function Ips2geoscallback (err, geos) {
if (err) {
Handlererror (ERR)
} else {
Geos2weathers (GEOS, Geos2weatherscallback)
}
}
function Geos2weatherscallback (err, weathers) {
if (err) {
Handlererror (ERR)
} else {
Writeweather (Weathers, Writeweathercallback)
}
}
function Writeweathercallback (err) {
if (err) {
Handlererror (ERR)
} else {
Console.log (' success! ')
}
}
Readip ('./ip.json ', Readipcallback)
Well, that's all we callback.js. Run:
Copy Code code as follows:
The Weater.json file will be generated:
Copy Code code as follows:
[
{
"IP": "180.153.132.38",
"Weather": "Clear",
"Region": "Shanghai"
},
{
"IP": "91.239.201.98",
"Weather": "Clouds"
},
{
"IP": "60.28.215.115",
"Weather": "Clear",
"Region": "Tianjin"
},
{
"IP": "74.125.235.224",
"Weather": "Clouds",
"Region": "California"
},
{
"IP": "115.29.230.208",
"Weather": "Clear",
"Region": "Zhejiang"
}
]
What is the real problem?
Asynchronously, of course, there are three things to deal with in nature:
1. When the asynchronous operation ends, need to be notified back, Callback is a scheme;
2. The results of asynchronous production need to be passed back, Callback accept a data parameter, pass back the information;
3. What if there is an error in the asynchronous? Callback accepts a err parameter and passes the error back.
But did you find a lot of repetitive work (various callback)? Are there any problems with the above code? Please look forward to the sequel of this article.