nodejs q模块

  • nodejs module q

    • q模块

      • promise
      • q模块的安装
      • promise的使用
        • then 函数
        • 流式操作
        • 组合操作
      • Promise的创建
        • Using Qfcall
        • Using Deferreds
        • Using QPromise
      • 实际例子
        • 测试服务器
        • 并行请求
        • 串行请求
        • 延时操作

接触nodejs时间不长,如果有所纰漏,请大家批评指正

nodejs module q

众所周知,nodejs是异步的,但是何为异步呢?就是设置一个任务后立即返回,然后加上一个监听,当任务结束的时候,就去调用监听。

比如下面的代码:

fs = require(‘fs‘)
fs.readFile(‘/etc/hosts‘, ‘utf8‘, function (err,data) { // 设置了一个监听,文件读取完毕以后调用
  if (err) {
    return console.log(err);
  }
  console.log(data);
}); // 不阻塞,马上返回

console.log("starting read file...");

// result:
// starting read file...
// host file content

要是对于简单的任务,这是一个非常好的设计,给你一个任务,任务做好了马上告诉我,然后出发相应的操作

但是如果对于复杂的任务,就未必是一种好的做法了。比如我们现在有多个任务需要进行处理,但是这些任务之间存在以来关系,比如任务二需要在任务一完成以后才可以开始,任务三要在任务二后面才可以开始。。。如果按照上面回调函数的写法:

task1(function (value1) {
    task2(value1, function(value2) {
        task3(value2, function(value3) {
            task4(value3, function(value4) {
                // Do something with value4
                // ... more task ...
                // I am in the callback hell
            });
        });
    });
});

这种写法看起来虽然简单,但是如果在各个任务中都含有多个复杂的逻辑操作,需要串行操作的任务一旦变多,那么这种回调的写法就可读性非常差,而且难以维护。也就是所谓的回调地狱

拿有没有什么方法可以简化这种复杂的操作呢?答案是肯定的,那就是nodejs里面的q模块。

q模块

q模块不仅仅是为了解决回调地狱的问题,它还能很大程度上辅助你进行一些需要并行,串行,定时等操作。

promise

promise是用来取代回调函数来进行异步操作的另一种方案

我们先来看一下大牛对promise的定义

A promise is an abstraction for asynchronous programming. It’s an object that proxies for the return value or the exception thrown by a function that has to do some asynchronous processing. — Kris Kowal on JSJ

我们做什么事都不要忘了最初的目的,我们最初采用回调的目的就是布置一件任务,任务结束以后,就将操作的数据传入我们注册的函数,我们再去处理数据。

promise做的事情也是同样的目的,为了异步操作得到数据,首先布置一件任务,然后返回一个promise对象,该promise对象承诺一定给我一个结果,要是是任务成功将结果返回给我,要么就是任务执行失败,返回一个异常信息。

所以只要我们有这个promise对象,我们就可以在任何地方处理promise返回给我们的结果,就是这么优雅。换句话说,就是将任务布置的代码和任务结果的处理代码进行了分离。

我们来看一个例子

function myReadFile(){
    var deferred = Q.defer();
    FS.readFile("foo.txt", "utf-8", function (error, text) {
        if (error) {
            deferred.reject(new Error(error));
        } else {
            deferred.resolve(text);
        }
    });
    return deferred.promise; // 这里返回一个承诺
}
/**
* many many code here
*/
promise.then(function(data){
    console.log("get the data : "+data);
},function(err){
    console.err(err);
});

好啦,既然知道什么是promise了,我们就开始探讨一下q模块

q模块的安装

安装nodejs

node官网下载安装nodejs

新建一个工程,填写一基本信息

mkdir qtest && cd qtest && npm init

安装q模块

npm install q –save

promise的使用

下面是博主在学习q模块的时候所见所得,可能有所纰漏,如果需要q模块的全面资料,大家可以参见这里

then 函数

我们知道,当得到一个promise以后,我们需要指定对应的处理函数,也就是用then函数来指定对应的处理函数。

then函数传入两个函数对象:

  • promisefulfilled的时候执行的函数
  • promiserejected的时候执行的函数

每次只有一个函数可能被执行,因为返回的promise只可能存在一个状态,要么被promise被解决了,要么promise没有被解决。

最终then函数返回一个新的promise

如下面所示:

var outputPromise = getInputPromise()
.then(function fulfilled_function(input) {// 传入两个函数对象,一个用来处理fulfilled情况,一个处理rejected情况
}, function fulfilled_function(reason) {
}); // 最终返回一个新的promise

在传入的fulfilled_functionrejected_function函数中,函数的返回值会影响then函数返回的promise(也就是这里的outputPromise)的行为:

  • 如果返回一个普通值,promise就是fulfilled
  • 如果抛出一个异常,那么promise就是rejected
  • 如果返回一个新的promise,那么then函数返回的promise将会被这个新的promise取代。

如果你只关心任务的成功或者失败状态,上面的fulfilled_function或者rejected_function可以设置为空。

我们来看几个例子:

返回一个普通值:

var Q = require(‘q‘);

var outputPromise = Q(1).then(function(data){
   console.log(data);
   return 2; // outputPromise将会fulfilled
});

outputPromise.then(function(data){
    console.log("FULFILLED : "+data);
},function(err){
    console.log("REJECTED : "+err);
})
/** 运行结果
1
FULFILLED : 2
*/

抛出一个异常

var Q = require(‘q‘);

var outputPromise = Q(1).then(function(data){
   console.log(data);
   throw new Error("haha ,error!");
   return 2;
});

outputPromise.then(function(data){
    console.log("FULFILLED : "+data);
},function(err){
    console.log("REJECTED : "+err);
})
/** 运行结果
1
REJECTED : Error: haha ,error!
*/

返回一个新的promise

var Q = require(‘q‘);

var outputPromise = Q(1).then(function(data){
   console.log(data);
   return Q(3);
});

outputPromise.then(function(data){
    console.log("FULFILLED : "+data);
},function(err){
    console.log("REJECTED : "+err);
})

/** 运行结果
1
FULFILLED : 3
*/

流式操作

上面提到过then函数最后会返回一个新的promise,这样我们就可以将多个promise串联起来,完成一系列的串行操作。

如下面所示:

return getUsername()
.then(function (username) {
    return getUser(username);
})
.then(function (user) {
    // if we get here without an error,
    // the value returned here
    // or the exception thrown here
    // resolves the promise returned
    // by the first line
});

组合操作

假如我们现在有多个任务需要一起并行操作,然后所有任务操作结束后,或者其中一个任务失败后就直接返回,q模块的中的all函数就是用来解决这个问题的。

我们来几个例子

成功执行:

var Q = require(‘q‘);

function createPromise(number){
    return Q(number*number);
}

var array = [1,2,3,4,5];

var promiseArray = array.map(function(number){
    return createPromise(number);
});

Q.all(promiseArray).then(function(results){
    console.log(results);
});

/** 运行结果
[ 1, 4, 9, 16, 25 ]
*/

其中某个抛出异常:

var Q = require(‘q‘);

function createPromise(number){
    if(number ===3 )
        return Q(1).then(function(){
            throw new Error("haha, error!");
        })
    return Q(number*number);
}

var array = [1,2,3,4,5];

var promiseArray = array.map(function(number){
    return createPromise(number);
});

Q.all(promiseArray).then(function(results){
    console.log(results);
},function (err) {
    console.log(err);
});
/** 运行结果
[Error: haha, error!]
*/

但是有些时候我们想等到所有promise都得到一个结果以后,我们在对结果进行判断,看看那些是成功的,那些是失败的,我们就可以使用allSettled函数。

如下所示:

var Q = require(‘q‘);

function createPromise(number){
    if(number ===3 )
        return Q(1).then(function(){
            throw new Error("haha, error!");
        })
    return Q(number*number);
}

var array = [1,2,3,4,5];

var promiseArray = array.map(function(number){
    return createPromise(number);
});

Q.allSettled(promiseArray)
    .then(function (results) {
        results.forEach(function (result) {
            if (result.state === "fulfilled") {
                console.log(result.value);
            } else {
                console.error(result.reason);
            }
        });
    });
/** 运行结果
1
4
[Error: haha, error!]
16
25
*/

或者有时候我们只需要其中一个promise有结果即可,那么any函数就比较适合我们

如下所示:

var Q = require(‘q‘);

function createPromise(number){
    if(number ===3 || number === 1 )
        return Q(1).then(function(){
            throw new Error("haha, error!");
        })
    return Q(number*number);
}

var array = [1,2,3,4,5];

var promiseArray = array.map(function(number){
    return createPromise(number);
});

Q.any(promiseArray).then(function(first){
    console.log(first);
},function(error){
    console.log(error); // all the promises were rejected
});

/** 运行结果
4
*/

Promise的创建

上面我们讲到的都是promise的使用,那么如何创建一个新的promise呢,q模块里面提供好几种方法来创建一个新的promise

  • Using Q.fcall
  • Using Deferreds
  • Using Q.promise

Using Q.fcall

你可以使用fcall来直接创建一个将会fullfilledpromise

return Q.fcall(function () {
    return 10;
});

也可以创建一个将会rejectedpromise

return Q.fcall(function () {
    throw new Error("Can‘t do it");
});

或者才创建一个promise的时候传入自定义参数:

return Q.fcall(function(number1 , number2){
    return number1+number2;
}, 2, 2);

Using Deferreds

上面我们提到的fcall方法来创建promise的方法,虽然简单,但是在某些时候不一定能满足我们的需求,比如我们现在创建一个新的promise,需要在某个任务完成以后,让promise变成fulfilled或者是rejected状态的,上面的fcall方法就不适合了,因为它是直接返回的。

那么这里使用Deferred来实现这种操作就再合适不过了,我们首先创建一个promise,然后在合适的时候将promise设置成为fulfilled或者rejected状态的。

如下所示:

var deferred = Q.defer();
FS.readFile("foo.txt", "utf-8", function (error, text) {
    if (error) {
        deferred.reject(new Error(error));
    } else {
        deferred.resolve(text);
    }
});
return deferred.promise;

Using Q.Promise

最后一个要介绍的就是Q.Promise函数,这个方法其实是和Deferred方法比较类似的,我们要传入一个带有三个参数的函数对象,分别是resolve,reject,notify。可以调用这三个函数来设置promise的状态。

看个例子:

var Q = require(‘q‘);
var request = require(‘request‘);

function requestUrl(url) {
    return Q.Promise(function(resolve, reject, notify) {
        request(url, function (error, response, body) {
            if(error)
                reject(error);
            if (!error && response.statusCode == 200) {
                resolve(body);
            }
        })
    });
}

requestUrl(‘http://www.baidu.com‘).then(function(data){
    console.log(data);
},function(err){
    console.error(err);
});
/** 运行结果
百度首页html内容
*/

实际例子

这里我们将会模拟串行和并行请求多个url地址。

测试服务器

我在本地搭建了一个测试用的express服务器:,对应代码如下

var express = require(‘express‘);
var router = express.Router();

/* GET home page. */
router.get(‘/address1‘, function(req, res, next) {
    res.send("This is address1");
});

router.get(‘/address2‘, function(req, res, next) {
    res.send("This is address2");
});

router.get(‘/address3‘, function(req, res, next) {
    res.send("This is address3");
});

module.exports = router;

并行请求

var Q = require(‘q‘);
var request = require(‘request‘);

var urls = [
    ‘http://localhost:3014/q-test/address1‘,
    ‘http//localhost:3014/q-test/address2‘, // this is wrong address
    ‘http://localhost:3014/q-test/address3‘
];

function createPromise(url){
    var deferred = Q.defer();
    request(url , function(err , response , body){
        console.log("requested "+url);
        if(err)
            deferred.reject(err);
        else
            deferred.resolve(body);
    });
    return deferred.promise;
}

var promises = urls.map(function (url) {
    return createPromise(url) ;
});

Q.allSettled(promises) .then(function (results) {
    results.forEach(function (result) {
        if (result.state === "fulfilled") {
            console.log(result.value);
        } else {
            console.error(result.reason);
        }
    });
});
/** 运行结果
requested http//localhost:3014/q-test/address2
requested http://localhost:3014/q-test/address1
requested http://localhost:3014/q-test/address3
This is address1
[Error: Invalid URI "http//localhost:3014/q-test/address2"]
This is address3
*/

串行请求

var Q = require(‘q‘);
var request = require(‘request‘);

var urls = [
    ‘http://localhost:3014/q-test/address1‘,
    ‘http//localhost:3014/q-test/address2‘, // this is wrong address
    ‘http://localhost:3014/q-test/address3‘,
    ‘done‘ // append a useless item
];

function createPromise(url){
    var deferred = Q.defer();
    request(url , function(err , response , body){
        if(err)
            deferred.reject(err);
        else
            deferred.resolve(body);
    });
    return deferred.promise;
}

urls.reduce(function(soFar , url){
   return soFar.then(function(data){
       if(data)
           console.log(data);
       return createPromise(url);
   } ,function(err){
       console.error(err);
       return createPromise(url);
   })
},Q(null));
/** 运行结果
requested http://localhost:3014/q-test/address1
This is address1
requested http//localhost:3014/q-test/address2
[Error: Invalid URI "http//localhost:3014/q-test/address2"]
requested http://localhost:3014/q-test/address3
This is address3
requested done
*/

延时操作

下面我们使用q模块来对express服务器进行延时操作:

var express = require(‘express‘);
var router = express.Router();
var Q = require(‘q‘);

function myDelay(ms){ // 定义延时操作,返回promise
    var deferred = Q.defer() ;
    setTimeout(deferred.resolve , ms);
    return deferred.promise;
}

/* GET home page. */
router.get(‘/address1‘, function(req, res, next) {
    myDelay(5000).then(function(){
        res.send("This is address1"); // 时间到了就返回数据
    });
});

router.get(‘/address2‘, function(req, res, next) {
    res.send("This is address2");
});

router.get(‘/address3‘, function(req, res, next) {
    res.send("This is address3");
});

module.exports = router;

好,上面的串行和并行操作我们并没有看出什么区别,现在我们再来跑一遍程序:

并行结果

/** 运行结果
requested http//localhost:3014/q-test/address2
requested http://localhost:3014/q-test/address3
requested http://localhost:3014/q-test/address1
This is address1
[Error: Invalid URI "http//localhost:3014/q-test/address2"]
This is address3
*/

串行结果

/** 运行结果
requested http://localhost:3014/q-test/address1
This is address1
requested http//localhost:3014/q-test/address2
[Error: Invalid URI "http//localhost:3014/q-test/address2"]
requested http://localhost:3014/q-test/address3
This is address3
requested done
*/
时间: 2024-08-26 19:22:10

nodejs q模块的相关文章

Nodejs mkdirP 模块导致CPU占用高的问题

Nodejs mkdirP 模块导致CPU占用高的问题 近期将nodejs项目部署到服务器上并启动时,发现node进程的cpu占用率在40%左右,当时表示非常不解,刚启动的服务并没有运行什么需要大量消耗cpu的逻辑,且此时还未有请求发送到服务器端. 鉴于这种情况,只能猜测是某段程序在初始化一些东西的时候异常,所以才导致了这种情况. 经过对代码的排查后,最终锁定出为题的代码块如下: router.use(multer({ dest: config.uploadDir, limits:{ fileS

配置 Windows 下的 nodejs C++ 模块编译环境 安装 node-gyp

配置 Windows 下的 nodejs C++ 模块编译环境 根据 node-gyp 指示的 Windows 编译环境说明, 简单一句话就是 "Python + VC++ 编译环境". 所有需要的安装文件, 我都下载好放到百度云盘了: nodejs-gyp-windows Python 安装 python-2.7.7.msi iso 虚拟磁盘 安装 DTLite4491-0356.exe Windows XP 文件在: node-gyp-windows/windowsXP 用 DTL

nodejs cheerio模块提取html页面内容

nodejs cheerio模块提取html页面内容 Table of Contents 1. nodejs cheerio模块提取html页面内容 1.1. 找到目标元素 1.2. 美化文本输出 1.3. 提取答案文本 1.4. 最终代码 本文给出使用一个用cheerio模块提取html文件中指定内容的例子,并说明具体步骤.涉及到的API.以及其它模块. cheerio模块是一个类似jquery的模块,具有相似的API.功能,能够将一个网页解析为DOM,以及通过selector选择元素,设置.

NodeJS http 模块

#4 NodeJS http 模块 工作目录 server.js var http = require('http'); var fs = require('fs'); var path = require('path'); var mime = require('mime'); function send404(response){      response.writeHead(404,{           'Content-Type':'text/plain'      });     

nodejs的模块系统(实例分析exprots和module.exprots)

前言:工欲善其事,必先利其器.模块系统是nodejs组织管理代码的利器也是调用第三方代码的途径,本文将详细讲解nodejs的模块系统.在文章最后实例分析一下exprots和module.exprots. nodejs的模块 什么是模块? node.js通过实现CommonJS的Modules/1.0标准引入了模块(module)概念,模块是Node.js的基本组成部分.一个node.js文件就是一个模块,也就是说文件和模块是一一对应的关系.这个文件可以是JavaScript代码,JSON或者编译

Nodejs核心模块

全局对象 Nodejs中全局对象是 global(作为全局变量的宿主),所有全局变量都是global对象的属性,在Nodejs中能够直接访问到的对象通常都是global的属性,如 console,process等. 在最外层定义的变量 全局对象的属性: 隐式定义的变量(未定义直接赋值的变量) 当定义一个全局变量时,这个变量同时也会成为全局对象的属性. 注意:永远使用var定义变量,以避免引入全局变量,因为全局变量会污染命名空间,提高代码耦合的风险. 1.1.process process.arg

配置 Windows 下的 nodejs C++ 模块编译环境

根据 node-gyp 指示的 Windows 编译环境说明, 简单一句话就是 "Python + VC++ 编译环境". 所有需要的安装文件, 我都下载好放到百度云盘了: nodejs-gyp-windows Python 安装 python-2.7.7.msi iso 虚拟磁盘 安装 DTLite4491-0356.exe Windows XP 文件在: node-gyp-windows/windowsXP 用 DTLite 打开 VS2010Express1.iso , 然后安装

nodejs require模块找不到怎么解决?

用npm全局安装模块后,有时在程序使用的时候require会报找不到模块,要如何解决?nodejs是在module.paths包含的目录列表中寻找模块的,所以,要解决这个问题就是将全局模块加入到module.paths目录列表中. 全局安装模块后在程序里面require这个模块报错,模块找不到.如下图所示,首先按照正常的方式npm install -g express全局安装express模块.然后在测试require('express')报错 那么问题就出在nodejs怎么查找模块上.首先,我

NodeJS常用模块介绍

收集了NodeJS开发中常用的一些模块. MVC框架 - Express Express 是轻量灵活的Nodejs Web应用框架,它可以快速地搭建网站.Express框架建立在Nodejs内置的Http模块上,并对Http模块再包装,从而实际Web请求处理的 功能.它支持多种前端模板,如Jade, EJS等.它是T.J大神的作品,不过已经交由其它团队维护了. Koa是T.J大神的另外一个作品,号称下一 代NodeJS web框架.使用 koa 编写 web 应用,通过组合不同的 generat