最开始想使用Node.js中的promise是基于以下的问题背景:
访问一个web服务器,向其发送post请求,获取相应的response,解析得到的response以后,根据其中的label编辑不同的post请求,再向另一个web服务器发送新的post请求,获得相应的response,流程图如下:
在没有使用promise时,由于Node.js的函数调用都是异步调用,要实现上述功能,就需要使用嵌套的函数回调,伪代码如下:
getLabel(URL1, function(error, result) {
if(error) {
throw error;
} else {
getRes1(URL2, function(error, result) {
if(error) {
throw error;
} else {
getRes2(URL2, function(error, result) {
if(error) {
throw error;
} else {
......
}
});
}
});
}
});
显然,这样的写法是很不优雅的,有几个不同的label,就要几个回调函数,会使代码很冗长,而且很难维护,有没有简单明晰的方式能够完成这个任务呢?这个时候找到了promise。
Promise是什么,我的理解是promise其实也是一种回调,只是它给出了运行正常时的回调(通过resolve()到达),和运行错误时的回调(通过reject()到达),then()执行的就是将这两个回调连接起来。
每一个异步任务结束立刻返回一个promise对象,这个对象有一个then方法,允许调用指定的回调函数。某种程度上,这样的机制可以将若干异步的任务串行起来,使得前一个任务做完才能继续下一个任务。
这里不再赘述promise的原理和机制,promise的原理可以参照以下的链接,都讲解的很清楚:
- https://developers.google.com/web/fundamentals/getting-started/primers/promises?hl=zh-cn
- http://javascript.ruanyifeng.com/advanced/promise.html
接下来讲一下刚才的问题要如何通过promise来改写,先把代码贴出来:
/** Procedure 1
* Input : url of recognition website
* Output : recognition labels
**/
function GetLabel(url) {
return new Promise(
function (resolve, reject) {
/*......*/
fs.readFile(filepath, 'utf8', function (err, data){
/*......*/
var request = require('request');
request(options, callback =
function (error, response, data) {
if (!error && response.statusCode == 200) {
console.log('----info------', data);
resolve(data);
} else {
reject(error);
}
});
});
});
}
/** Procedure 2
* Input : Feedback result of every iteration
*
* Output : Accumulated result
**/
function GetNutrition(param) {
return new Promise(
function (resolve, reject) {
/*......*/
var request = require('request');
request(options, callback =
function (error, response, data) {
if (!error && response.statusCode == 200) {
console.log('----info------', data);
resolve(data);
} else {
reject(error);
}
});
});
}
/**
* Chain of the task
*/
GetLabel(URL1)
.then(
function (param) {
console.log(param);
return GetNutrition(param);
})
.then(
function (param) {
console.log(param);
return GetNutrition(param);
})
.then(
function (param) {
console.log(param);
return GetNutrition(param);
})
.then(function (param) {
res.json({
"result": jsonData.recommender
});
});
其中GetLabel()实现第一个任务,即取得URL1返回的label,成功以后将通过response得到的数据包直接返回给回调函数then(),并在then()中调用另一个promise GetNutrition(),通过这个promise回调下一个then()。其中的参数传递通过resolve()实现,代码中省去了catch。由此可以很好地将本来异步的实例串行化,也使得代码的结构更清晰一些。
下面有一个简单的小示例,用来总结这里介绍的promise的功能,代码如下:
var express = require('express');
var fs = require("fs");
var app = express();
/* has promise */
app.get('/', function (req, res) {
res.send('Has Promise!');
function SleepXSec (sec) {
return new Promise(
function (resolve, reject) {
setTimeout(function() { console.log("+%s", sec); resolve(sec-5); }, sec*1000);
});
}
SleepXSec(11)
.then(function(sec) {
return Sleep1Sec(sec);
})
.then(function(sec){
return Sleep1Sec(sec);
});
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
如果不使用promise,上述代码的结果应该是:
1s
6s
11s
但是由于使用了promise,强制确定了异步程序在上一个回调不结束之前不得进入下一步,因此输出变为:
11s
6s
1s
参考链接: