Promise到底是个什么梗?--node超级大菜鸟的革命之路
发布于 3 个月前 作者 zengming00 1375 次浏览 来自 分享
/*
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 这个模块

回到顶部