Promise到底是个什么梗?--node超级大菜鸟的革命之路
/*
Promise到底是个什么梗?
作为新手,这两天被promise弄得对Node.js的兴趣少了一半!百度'nodejs promise'出来的文章基本都是你抄我我抄你,没有一个能讲明白的,难道用一些通俗易懂的例子来解释就这么难吗?,我没学过es6,只会一些非常简单的js语法,我只想用node做点小玩意,难道不学es6就玩不了node吗?劳资偏不信这个邪!
*/
var Promise = require('bluebird');
var util = require('util');
/*一个简单得不能再简单的模拟的异步方法*/
function sum(p1, p2, callback){
setTimeout(function(){
var e = parseInt(Math.random()*10);//模拟随机产生异常
if(e){
callback(null, p1+p2);
}else{
callback(util.format("发生异常:p1=%d, p2=%d",p1,p2));
}
},1000);
}
//一般异步方法都是这样用的:(像fs、http等)
/*
sum(1, 2, function (err, sum) {
if(err){
console.log(err);
} else {
console.log(sum);
}
})
*/
/*
//而一些情况下我们真的会这样用:
sum(1, 2, function (err, sum) {
if(err){
console.log(err);
} else {
sum(sum, 3, function (err, sum) {
if(err){
console.log(err);
} else {
sum(sum, 4, function (err, sum) {
if(err){
console.log(err);
} else {
console.log(sum);
}
});
}
});
}
})
*/
/*
整个逻辑简单得不得了,而代码却像屎一样难看,但是下一步的操作总是依赖上一步的操作,虽然方法是异步的,但是你只能这样嵌套,你有更好的办法吗?请告诉我
这个逻辑就像是:
1.一个用户请求访问一个文件,但是我首先要判断一下文件是否存在,这就是第一个异步了
2.如果文件存在,我又去数据里查一下这个文件他能不能访问,这又是一个异步
3.如果可以访问,最后我再把这个文件发送出去,这又是一个异步
你说这种逻辑不是和php之类的一样吗?node的异步方式有个卵用!
是的,我也是这么认为的,但是“大家好才是真的好”,大家都说好,那必有其道理,是我等还没领悟到。那没领悟到之前,问题总得解决吧,
这种屎一样的代码除了电脑,谁看了都不爽.
先不说传说中的Promise库怎么用,想想connect或express对中间件的处理方式
app.use(中间件1)
app.use(中间件2)
app.use(中间件3)
app.use(异常处理中间件)
中间件怎么处理的呢?当前中间件处理不了或者完成了工作就转移到下一个中间件
function(req,res,next){ //一般中间件大概长这个样子,和http.createServer()那个回调不同的是,多了一个next参数
next(); //next是个函数,调用他之后就会把工作转移到下一个中间件,next可以传递参数,这参数一传就直接跳到异常处理去了!不传就是到下一个普通中间件
}
异常处理中间件
function(err,req,res,next){ //区别就是第一个参数是err,这种区别怎么判断呢? arguments.length判断函数的参数个数!!!就这么简单
next('error'); //和普通中间件一样,这里给下一个异常处理传了'error'字符串,就是第一个参数,普通中间件的next玩法一样
}
对于中间件我大概就说这么多了。基于这种思想,寡人自己发明了个轮子:
*/
/*
function Next(){
this.funcs = [];
}
Next.prototype.next = function(callback){
this.funcs.push(callback);
return this;
}
Next.prototype.start = function(){
var func = this.funcs.shift();
func && func.apply(this,arguments);
}
exports = module.exports = Next;
//用法很简单,next()就是不断的添加函数,start就是按顺序执行他们,
var next = new Next();
next.next(function(p1,p2){
sum(p1,p2,function(e,sum){
if(e){
console.log(e);
}else{
next.start(sum,3);
}
})
}).next(function(p1,p2){
sum(p1,p2,function(e,sum){
if(e){
console.log(e);
}else{
next.start(sum,4);
}
})
}).next(function(p1,p2){
sum(p1,p2,function(e,sum){
if(e){
console.log(e);
}else{
console.log(sum);
}
})
})
next.start(1,2)
*/
/*
程序还是那个程序,但是层次结构清晰多了,毕竟node.js也有些年头了,我等菜鸟没有必要重新发明轮子
var Promise = require('bluebird'); //bluebird就是一个现成的Promise,拿过来用
Promise据说就能解决坑爹的回调问题,但是,寡人却看不懂网上别人写的文章到底TM是什么意思!
Promise大概说的是这样一种结构:
Promise(function(){
//异步操作1
}).then(function(data){
//异步操作2,可以收到上面那个异步操作的结果
}).then(function(data){
//异步操作3,可以收到上面那个异步操作的结果
}).catch(function(e){
//如果上面的任何一步发生异常就直接到这里了
})
*/
//这就是一个基本的用法
/*
new Promise(function(resolve, reject){ //原理和我的轮子差不多,不过调用下一步的方法放到了函数参数上,我不清楚这有什么好处
sum(1, 2, function(err, sum){
if (err) {
reject(err);
} else {
resolve(sum)
}
});
}).then(function(data){
console.log(data);
}).catch(function(err){
console.log(err);
});
*/
//问题就TM来了,是啊,这是把回调拆开来了,可是then里面没有resolve, reject了,下一步呢?
/*
new Promise(function(resolve, reject){
sum(1, 2, function(err, sum){
if (err) {
reject(err);
} else {
resolve(sum)
}
});
}).then(function(data){
console.log(data);
}).then(function(data){ //我先不管下一步怎么办,上面不是说是.then().then()这种结构嘛,我毫不犹豫地就复制了一份
console.log(data); //输出undefined,尼码,还真执行了,但是上一步没有resolve来传递参数啊!
}).catch(function(err){
console.log(err);
});
*/
//经过研究,原来是要在then()里再返回一个Promise对象
/*
new Promise(function(resolve, reject){
sum(1, 2, function(err, sum){
if (err) {
reject(err);
} else {
resolve(sum)
}
});
}).then(function(data){
console.log(data);
return new Promise(function(resolve, reject){ //屎一样的代码,原来是要这么用!
sum(data, 3, function(err, sum){
if (err) {
reject(err);
} else {
resolve(sum)
}
});
})
}).then(function(data){
console.log(data);
}).catch(function(err){ //原来catch真的可以在上面任何一个地方出错时中断顺序直接到达这里,这真的不好理解,按理来说别的Promise对象发生什么事不归他管啊?
console.log(err);
});
*/
/*现在,让我们的代码变得更加优雅一点吧
//对sum函数进行包装
function psum(p1,p2){
return new Promise(function(resolve,reject){
sum(p1, p2, function(err,s){
if(err){
reject(err);
}else{
resolve(s);
}
});
});
}
psum(1,2).then(function(data){
console.log(data);
return psum(data,3);
}).then(function(data){
console.log(data);
return psum(data,4);
}).then(function(data){
console.log(data);
//return ; //达到最终目的,没有必要再返回什么东西了
}).catch(function(err){
console.log(err);
});
//当然bluebird给我们提供了更简单的方式帮我们自动完成了,你可以试试用下面这行代码来代替我们手动包装的psum
//var psum = Promise.promisify(sum);
*/
以上这就是Promise的一个基本用法了。
/*
//然而,事情并不是我们想像中那么美好,定义一个ret函数,只是简单的把两个参数原封不动返回
function ret(p1, p2, callback){
setTimeout(function(){
var e = parseInt(Math.random()*10);//模拟随机产生异常
if(e){
callback(null, p1, p2);
}else{
callback(util.format("发生异常:p1=%d, p2=%d",p1,p2));
}
},1000);
}
//var pret = Promise.promisify(ret);
var pret = function(p1,p2){
return new Promise(function(resolve,reject){
ret(p1,p2,function(e, a, b){
if(e){
reject(e);
}else{
resolve(a,b);
}
});
});
};
pret(11,22).then(function(a,b){ //我想当然的以为a,b会传回来,然而并没有,这就是说resolve只能传递一个参数
console.log(a,b); //11 undefined
console.log(arguments); //{ '0': 11 }
}).catch(function(e){
console.log(e);
});
*/
/*
Promise还有一个网上传说中的用法
**无关系汇总任务**
*/
function foo(p1, p2, callback){//还是一个异步的加法
var str = 'foo('+p1+','+p2+')';
var delay = (p1==1) ? 3000 : 1000;//故意让pfoo(1,2)返回得慢一些
console.log(str + '异步开始');
setTimeout(function(){
console.log(str + '异步完成');
callback(null, p1+p2);
},delay);
}
var pfoo = Promise.promisify(foo);//Promise化foo函数
var promise1 = pfoo(1,2);
var promise2 = pfoo(3,4);
var arr = [ promise1, promise2 ];
Promise.all(arr).then(function(datas){
console.log(datas);
var sum = 0;
for(var i in datas){
sum += datas[i];
}
console.log('sum=' + sum);
});
/*
结果:
foo(1,2)异步开始
foo(3,4)异步开始
foo(3,4)异步完成
foo(1,2)异步完成
[ 3, 7 ]
sum=10
可以看到两次调用是‘同时执行’的,而不是一个等待另一个执行完毕再执行
这样一来上面所说的:
“1.一个用户请求访问一个文件,但是我首先要判断一下文件是否存在,这就是第一个异步了
2.如果文件存在,我又去数据里查一下这个文件他能不能访问,这又是一个异步
3.如果可以访问,最后我再把这个文件发送出去,这又是一个异步”
就可以优化了:
同时两个异步任务,一个检查文件是否存在,另一个去查数据库
两个任务都完成了再根据两个任务的结果决定最终的处理方式,这样,node异步方式的优势似乎又回来了。
以上内容均属个人观点,如有雷同纯属巧合,如有异议者请详细说明,寡人读书少,网上的Promise真的太概念化了,对新手一点都不友好
2016/09/04 19:05 入node坑一星期左右,之前几乎没用js做过什么事情
*/
12 回复
顶楼主,分析的很详细
楼主能不能用bluebird写个简单留言板什么的,新手看看例子,就明白用法了。跪求
楼主好样的 解决了我很多疑惑
来自酷炫的 CNodeMD
用async 完美解决你的问题
专门登录上来顶楼主。之前也有过类似疑惑,同样经历的默默飘过。
promise是挺绕的
cnode 的评论列表没有分页的吗
楼主想的深,赞一个。网上的那些Promise太概念化了,不友好。 新手也可以看看这个:http://www.cnblogs.com/lvdabao/p/es6-promise-1.html
语法糖。
var request = require('request');
var url = 'http://localhost:3000/';
request(url, function(err, resp, body) {
if (err) {
console.log(err)
} else {
if(resp.statusCode != 200){
console.log(resp.headers);
}else {
console.log(body);
}
}
})
function reqify(url) {
return new Promise(function(resolve, reject) {
request(url, function(err, resp, body) {
if (err) {
reject(err);
} else {
resolve(resp);
// body 该放哪?
}
});
});
}
reqify(url).then(function(resp) {
console.log(resp.headers);
});
如何在promise 中使用callback中两个以上回调的参数的返回 ?
Promise.then( onresolved; onrejected ); 对于 onresolved 和 onrejected这两个函数的返回值有什么作用? 楼主能否解答一下?如有大神看到,也请解答一下
@yakczh 返回数组
或者你可以用 request-promise 这个模块