男神觉得时机成熟了,手捧99朵披着月季的玫瑰向女神求婚:
“小妞妞,嫁给我吧!我发誓,我会对你一辈子好的!”
“这个嘛,你先去问问我爸爸,我大伯以及我大姑的意思,他们全部都认可你,我再考虑考虑!对了,如果我爸没有答复,大伯他们肯定是不会同意的;如果大伯没有答复,大姑也是不会同意的。”
在传统的JS中,类似这种多嵌套,多异步,多发展的,我们需要牺牲(对于程序来讲)微不足道的时间换取代码的空间和易维护性。
因为得到长辈同意是需要等待的,而且一次只能攻略一个长辈,而且还能重复攻略。如果我们使用当下的JS去套用现实逻辑,可能就会这样子(无需细看):
男神.请求({ 姓名: "岳父", 成功: 男神.继续请求({ 姓名: "大伯", 成功: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: "求婚失败" }) }), 失败: 男神.继续请求({ 姓名: "大伯", 成功: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: "求婚失败" }) }), 失败: "求婚失败" }) }), 失败: 男神.请求({ 姓名: "岳父", 成功: 男神.继续请求({ 姓名: "大伯", 成功: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: "求婚失败" }) }), 失败: 男神.继续请求({ 姓名: "大伯", 成功: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: 男神.继续请求({ 姓名: "大姑", 成功: 男神.最终请求({ 姓名: "女神", 成功: "求婚成功", 失败: "求婚失败" }), 失败: "求婚失败" }) }), 失败: "求婚失败" }) }), 失败: "求婚失败" }) })
为啥JS呈现的会这么复杂呢?
- 代码需要预先设定好未来要发生的事情(先知视角);但是现实世界并不会如此(经历者视角);
- 代码中,空间是稀缺资源;而现实世界中,时间才是。
所以,传统JS中,为了实现上面的嵌套,就需要这么做:
var Nanshen = {
"身高": 180,
"体重": 80,
"年薪": "200k",
request: function (obj) {
if (Math.random() > 0.2) { // 假设男神每次攻略,成功的概率为0.8
obj.success();
} else {
obj.error;
}
}
};
var Request = function (names, success) {
// names是一个数组,这里是["岳父", "大伯", "大姑"]
var index = 0,
first = 0; // 每个亲戚有两次攻略机会
var request = function () {
if (names[index]) {
Nanshen.request({
name: name[index],
success: function () {
first = 0;
console.log("成功拿下" + names[index]);
index++;
request(); // 如果成功,攻略下一个亲戚
},
error: function () {
if (first == 1) {
console.log("依旧没能拿下" + names[index]);
// 某亲戚两次攻略都失败,则求婚失败,退出求婚嵌套
return;
} else {
console.log("没能拿下" + names[index] + ",再试一次");
}
first = 1;
request(); // 如果成功,攻略下一个亲戚
}
});
} else {
success();
}
};
request();
};
Request(["岳父", "大伯", "大姑"], function () { // 传入参数:亲戚数组,调用上面的循环
Nanshen.request({
name: "女神",
success: function () {
console.log("女神同意,求婚成功!");
},
error: function () {
console.log("女神不同意,求婚失败!");
}
});
});
想要用一种“先知”的角度去实现这个求婚历程,也就是实现现实世界中的思维:“搞定岳父→搞定大伯→搞定大姑→搞定女神”,请参考:https://www.zhangxinxu.com/wordpress/2014/02/es6-javascript-promise-%E6%84%9F%E6%80%A7%E8%AE%A4%E7%9F%A5/
Promise去实现我们的求婚过程也就是像下面这样:
// 男神的各项参数
var NanShen = {
"身高": 180,
"体重": 80,
"年薪": "200K",
request: function(obj) {
// 成功与否随机决定
// 执行成功的概率为80%
if (Math.random() > 0.2) {
obj.success();
} else {
obj.error();
}
}
};
var Request = function(name) {
return new Promise(function(resolve,reject) {
var failed = 0; request = function() {
NanShen.request({
name: name,
success: function() {
console.log(name + '攻略成功!');
failed = 0;
resolve();
},
error: function() {
if(failed === 0) {
console.log("第一次攻略" + name + "失败,重试一次!");
failed === 1;
// 重新攻略一次
request();
} else {
console.log("依然没有拿下" + name + ",求婚失败!");
reject();
}
}
});
};
request();
});
};
Rrequest("岳父") // 搞定岳父,然后...
.then(function() { return Request("大伯"); }) // 搞定大伯,然后...
.then(function() { return Request("大姑"); }) // 搞定大姑,然后...
.then(function() {
NanShen.request({
name: "女神",
success: function() {
console.log("女神同意,求婚成功!");
},
error: function() {
console.log("女神不同意,求婚失败!"):
}
});
});
哈,忘记demo了,您可以狠狠地点击这里:男神求婚历程的Promise实现 点击刷新测试你的人品吧!
看上去代码量和传统JS实现不分伯仲,但是注意下面这段代码:
当局者思维的代码呈现就是异步回调形式,先知思维的代码呈现就是Promise形式。
如果套用求婚的故事,则:
- 先知思维(第三人称视角)——搞定岳父→搞定大伯→搞定大姑→搞定女神
- 当局者思维(第一人称视角)——去搞定岳父→…等待期…结果来了…→去搞定大伯→…等待期…结果来了…→去搞定大姑→…等待期…结果来了…→去搞定女神→…等待期…结果来了…
前者是先知是上帝,在规划;后者是凡人,在经历。
还是那句话,Promise只是让我们当下的JS换了一种形式实现,更符合编程时候的思考角度与方式。因此,通过特定的转换,我们也可以让不支持Promise的低版本浏览器支持之。您可以试试在页面引入如下JS:
<script src="http://s3.amazonaws.com/es6-promises/promise-0.1.1.min.js"></script>