用Async解决回调问题

本文探讨了同步编程的局限性及其在处理耗时任务时的问题,引出了异步编程的概念。通过示例解释了Node.js中基本的回调函数用法,指出其在处理多个任务时可能导致的代码混乱。然后,文章介绍了Async.js库,展示了如何用它来解决回调地狱问题,通过`async.series`和`async.parallel`简化代码并提高效率。最后,讨论了异步编程的重要性,鼓励开发者掌握异步编程技巧,以提升JavaScript代码的可读性和可维护性。
摘要由CSDN通过智能技术生成

概述

640?wx_fmt=png

第一次接触编程时,我们就知道了一块代码是从头执行到尾的。 这就是所谓的同步编程:每个操作完成之后,后面的才会继续。 对于不花计算机太多时间的操作,比如数字相加、操作字符串、或变量赋值等等,这种执行过程没什么问题。


但如果一个任务花的时间稍微长一点,你该怎么办呢?比如访问磁盘上的一个文件,发送一个网络请求,或等待一个计时器结束。 在同步编程中,这时候你的程序啥也做不了,只能干等着。


对于一些简单的情况,你的程序可以有多个实例同时在跑,或许还能忍受,但是对于很多服务器应用来说,这就是个噩梦。 


进入异步编程 在异步执行的程序中,你的代码在等待某件事的同时可以继续执行,然后这件事发生了你又可以跳回去。


以网络请求为例。 向一个较慢的服务器发送一个网络请求,可能足足要花三秒钟才能响应,你的程序可以在这个慢服务器响应的同时继续干其它的事。 在这个例子中,三秒钟对人来说或许算不了什么,但服务器不一样,它可能还等着响应上千个其它请求呢。 那么,你要如何在Node.js中处理异步呢? 


最基本的方式是使用回调。 一个回调其实就是一个函数,只不过它是在一个异步操作完成时被调用。 按惯例,Node.js的回调函数至少应该有一个参数,err。 回调可以有更多的参数 (通常表示传递给回调函数的数据),但至少应该有一个是err。 你可能已经猜到了,err表示一个错误对象 (当发生了一个错误时就会产生这样一个对象,后面还会提到)


我们来看一个非常简单的例子。 我们要用到Node.js内置的文件系统模块fs。 在此脚本中,我们会去读一个文本文件的内容。 此代码的最后一行是一个console.log,那么问题来了:如果你执行这个脚本,你会在看到文件内容之前看到这个日志结果吗?

var fs  = require('fs');
fs.readFile(
   'a-text-file.txt',      //the filename of a text file that says "Hello!"
   'utf8',                 //the encoding of the file, in this case, utf-8
   function(err,text) {    //the callback
       console.log('Error:',err);    //Errors, if any
       console.log('Text:',text);    //the contents of the file
   }
);
//Will this be before or after the Error / Text?
console.log('Does this get logged before or after the contents of the text file?');

因为它是异步的,我们实际上会在看到文本内容之前就看到最后一句console.log的执行了。 如果你在该脚本的同一目录下有一个名为a-text-file.txt的文件,你会看到err值为null,而text的值为此文本的内容。


如果不存在a-text-file.txt文件,err为一个Error对象,而text的值是undefined。 这种情况产生了一类重要的回调:因为错误无处不在,你总是要处理它们,回调就是一种重要方式。 为处理错误,你需要检查err变量的值,如果它有非nul值,则说明有错误发生了。 一般来说,err参数不会是false,所以总可通过真值检测来判断是否有错。

var fs  = require('fs');
fs.readFile(
   'a-text-file.txt',      //the filename of a text file that says "Hello!"
   'utf8',                 //the encoding of the file, in this case, utf-8
   function(err,text) {    //the callback
       if (err) {
           console.error(err);           //display an error to the console
       } else {
           console.log('Text:',text);    //no error, so display the contents of the file
       }
   }
);

又比如说你想按照一定的顺序展示两个文件的内容。 你会得到类似于这样的代码:

var fs  = require('fs');
fs.readFile(
   'a-text-file.txt',      //the filename of a text file that says "Hello!"
   'utf8',                 //the encoding of the file, in this case, utf-8
   function(err,text) {    //the callback
       if (err) {
           console.error(err);           //display an error to the console
       } else {
           console.log('First text file:',text);    //no error, so display the contents of the file
           fs.readFile(
               'another-text-file.txt',  //the filename of a text file that says "Hello!"
               'utf8',                   //the encoding of the file, in this case, utf-8
               function(err,text) {      //the callback
                   if (err) {
                       console.error(err);                       //display an error to the console
                   } else {
                       console.log('Second text file:',text);    //no error, so display the contents of the file
                   }
               }
           );
       }
   }
); 

这个代码不仅看起来太丑,且存在不少问题:

你是在串行加载文件;如果同时加载并在都加载完时返回,效率会更高。


语法上正确,可读性却极差。 注意那嵌套函数的数目,和不断深入的缩进,想想就可怕。 你可以用一些技巧让它看起来更好一些,但又会牺牲一些其他方面的可读性。


这种写法不是通用方式。 对于两个文件或许可行,但如果有9个文件呢?22个文件呢?1个呢? 当前这种写法就太不灵活了。


但别急,我们可以用async.js来解决所有这些问题 (也许还能解决其他一些问题呢)。

640?wx_fmt=png 640?wx_fmt=png

首先,让我们从安装async.js入手。

npm install async —-save

Async.js可将一系列函数粘连起来,既可以是串行,也可以是并行。 让我们重写前面的例子吧:

var async = require('async'),     //async.js module
   fs = require('fs');
async.series(                     //execute the functions in the first argument one after another
   [                               //The first argument is an array of functions
       function(cb) {                //`cb` is shorthand for "callback"
           fs.readFile(
               'a-text-file.txt',
               'utf8',
               cb
           );
       },
       function(cb) {
           fs.readFile(
               'another-text-file.txt',
               'utf8',
               cb
           );
       }
   ],

   function(err,values) {          //The "done" callback that is ran after the functions in the array have completed
       if (err) {                    //If any errors occurred when functions in the array executed, they will be sent as the err.
           console.error(err);
       } else {                      //If err is falsy then everything is good
           console.log('First text file:',values[0]);
           console.log
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值