Home  >  Article  >  Web Front-end  >  Node.js Asynchronous Programming Callback Introduction (1)_node.js

Node.js Asynchronous Programming Callback Introduction (1)_node.js

WBOY
WBOYOriginal
2016-05-16 16:07:121207browse

Node.js is based on JavaScript engine v8 and is single-threaded. Node.js uses asynchronous programming methods similar to JavaScript on the Web to handle blocking I/O operations. Reading files, accessing databases, network requests, etc. in Node.js may all be asynchronous. For those new to Node.js or developers migrating to Node.js from other language backgrounds, asynchronous programming is a painful part. This chapter will introduce you to all aspects of Node.js asynchronous programming from the simplest to the deeper. From the most basic callback to thunk, Promise, co to async/await planned for ES7.

First, let’s start with a specific example of asynchronous programming.

Get weather information for multiple IP locations

In the file ip.json, there is an array where we store several IP addresses, which are different visitors from different places. The content is as follows:

Copy code The code is as follows:

// ip.json
["115.29.230.208", "180.153.132.38", "74.125.235.224", "91.239.201.98", "60.28.215.115"]

I hope I can get the current weather for each IP location. Output the results to the weather.json file in the following format:
Copy code The code is 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" }
]

To organize our thoughts, we divide it into the following steps:

1. Read the ip address;
2. Get the geographical location of the IP based on the IP address;
3. Check the local weather based on geographical location;
4. Write the results to the weather.json file.

These steps are all asynchronous (reading and writing files can be synchronous, but as an example, asynchronous is used).

callback

First, we try to implement it in the way that Node.js API usually provides - passing a callback as an asynchronous callback without using any library. We will use three basic modules:

1.fs: Read the IP list from the file ip.json; write the result to the 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: used to assemble the url parameters for sending requests.

Create a new callback.js file and introduce these modules:

Copy code The code is 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 content, and then use JSON.parse to parse the JSON data:

Copy code The code is 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)
}
}
})
}
...

The next step is to use IP to obtain geo. We use request to request an open geo service:

Copy code The code is 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 The code is 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 obtained the interfaces for geo and weather, we still have a slightly more complicated problem to deal with. Because there are multiple IPs, we need to read geo in parallel and read weather data in parallel:
Copy code The code is 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
weather.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 items waiting to be returned. If remain is 0, it means that the parallel request has ended, and the processing results are loaded into an array and returned.

The last step is to write the results into the weather.json file:

Copy code The code is 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 the above functions, we can achieve our goal:

Copy code The code is 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!')
              }
            })
          }
        })
      }
    })
  }
})

哈哈,你妈这嵌套,你可能觉得这就是 JavaScript 异步的问题,说真的,嵌套不是 JavaScript 异步的真正问题所在。上面这段代码我们可以下面这样写:

复制代码 代码如下:

...
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)

好了,这是我们 callback.js 的全部内容。运行:

复制代码 代码如下:

node callback.js

将会生成 weater.json 文件:
复制代码 代码如下:

[
  {
    "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"
  }
]

Then what’s the real problem?

Of course it is a problem of asynchronous. Asynchronous essentially has to deal with three things:

1. When the asynchronous operation ends, it needs to be notified back. Callback is a solution;
2. The results generated asynchronously need to be passed back. Callback accepts a data parameter and passes the data back;
3. What should I do if an asynchronous error occurs? Callback accepts an err parameter and returns the error.

But have you found a lot of repetitive work (various callbacks)? Is there anything wrong with the above code? Please look forward to the continuation of this article.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn