nodejs中如何并行两个异步,等它们都完成后继续下面的操作?

https://segmentfault.com/q/1010000005943986?_ea=966070
上次我问过一个问题, 这个问题解答了一步一步通过promise异步后再完成的方法,但今天我突然又想到另一种情况,如果有好几个异步操作可以同时进行,但必须等它们全部完成后才能进行下面的操作,应该如何编码呢?设想下面的情况:
我们需要读几个文件,它们没有必然的先后关系,只要都读完就可以,但必须都读完才能进行下一步,请问该怎么做呢?

阅读 10.1k
8 个回答

可以用 Promise.all。
具体用法可以参考:链接描述

var getFile = function(filename) {
    return new Promise(function(resolve, reject){
        //读取文件
        resolve(filename + '中的文件内容');
    });
}
Promise.all([getFile('文件1'),getFile('文件2'),getFile('文件3')])
.then(function(res){
    //res:['文件1的内容','文件2的内容','文件3的内容']
})

then呀,链式执行。

一个插件:async

用async

async.parallel({
        swslist: function(callback){ 
            var param = {
                v_act: 'v_swslist',
                v_ldata: 'btype:0',
                v_lnum: 10,
                v_lpage: 1,
                v_mid: settings.Interface.mid
            };
            functions.dz_http_get(param, function(data){
                callback && callback(null, data)
            });
        },
        webIlist: function(callback){
            var param = {
                v_act: 'v_webIlist',
                v_id: 5,
                v_mid: settings.Interface.mid,
                v_type: 2
            };
            functions.dz_http_get(param, function(data){
                callback && callback(null, data)
            });
        }
    }, function(err, results){
        if(err || typeof results.swslist === 'string' || typeof results.webIlist === 'string'){
            res.render('error.html', { 
                errormsg: msg.parameter_error
            });
            return;
        }

        res.render('business/index.html', { 
            busList: results.swslist,
            articleList: results.webIlist,
            label_css: 'dz_label_swbc',
            label: msg.pagename.business
        });
    });

bluebird的Promise。Promise.all可以用来处理一组Promises,也可以用Promise.join,可以用来处理几个Promises的情况。
文档看这里

新手上路,请多包涵
var fs = require('fs');
var completedTasks = 0;
var tasks = [];

var filesDir = './text';

function checkIfComplete(){
    completedTasks++;
    if(completedTasks == tasks.length){   //读取文件任务完成
    
    }
}


fs.readdir(filesDir, function(err, files){
    if(err) throw err;
    for(var index in files ){
        var task = (function(file){
            return function(){
                fs.readFile(file, function(err, text){
                    if(err) throw err;
                    
                    checkIfComplete();
                })
            }
        })(filesDir + '/'+ files[index]);
        tasks.push(task);
    }

    for(var task in tasks ){
        tasks[task]();
    }
})

今天我也遇到了这个问题,看到了 无才狂士 推荐的地址去学习后,完成了自己的需求:

router.post('/', function(req, res, next) {
    var form = new multiparty.Form();
    form.uploadDir = tmpDir;

    form.parse(req, function(err, fields, files) {
      // console.log(util.inspect(fields, true));
      // console.log(util.inspect(files, true));
      
      ///将移除,移动文件封装成 promise.
      function rename(f){
        return new Promise(function(resolve,reject){
          if(f.size > 0){
            const fName = f.originalFilename;
            const newPath = uploadDir + fName;
            ///目标文件已经存在的话会被覆盖!
            fs.rename(f.path,newPath,function(err){
                if(err){
                  reject(err);
                  console.log('save file:' + newPath + ';err:' + err.toString());
                }else{
                  console.log('save file:' + newPath);
                  resolve(fName);
                }
            });
          }else{
            fs.unlink(f.path,function(err){
              if(err){
                reject(err);
                console.log('delete empty file:' + f.path + ';err:' + err.toString());
              }else{
                console.log('delete empty file:' + f.path);
                resolve('');
              }
            });
          }
        });
      }

      ///处理参数值,拿到的值包裹在数组里了,给去掉;
      Object.keys(fields).forEach(function(name) {
        const arr = fields[name];
        value = arr.length > 0 ? arr[0] : '';
        fields[name] = value;
      });

      ///处理文件,所有文件都放在一起;
      const fileList = [];
      Object.keys(files).forEach(function(name) {
        const arr = files[name];
        arr.forEach(function(v){
          fileList.push(v);  
        });
      });

      ///将文件数组映射为promise任务;
      const tasks = fileList.map(function(v){
        return rename(v);
      });

      ///任务全部完成后,给客户端响应;
      Promise.all(tasks).then(function(value){
        console.log('all:' + value.toString());
        // public/upload/Icon-24@2x.png,public/upload/Icon-29@3x.png,public/upload/Icon-40.png
        
        ///处理文件名,字符串转成数组,去掉空串,空串意味着客户端表单里没文件
        const fileNameArr = value.toString().split(',');
        for(var i = fileNameArr.length-1; i >= 0 ; i --){
          const e = fileNameArr[i];
          if(e.length == 0){
            fileNameArr.splice(i,1);
          }
        }
        ///构建响应结构体
        const result = {};
        result['status'] = 200;
        result['msg'] = 'received ' + fileNameArr.length + ' files!';
        result['ps'] = fields;
        result['files'] = fileNameArr;
        res.json(result);
        // res.writeHead(200, {'content-type': 'application/json'});
        // res.write(JSON.stringify(result));
        // res.end();

      }).catch(function(err){
        ///构建异常结构体
        const result = {};
        result['status'] = 5000;
        result['msg'] = err.toString();
        res.json(result);
      });
    });
  });

完整代码:https://github.com/debugly/SCNetworkKit/blob/master/Server/routes/upload.js

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏