Promise是专门给异步计算用的对象,它表示一个现在还没结果,但将来会给你算出来结果来的操作。
没有大肚子的人生是更好的人生
权尾珍
憋说话,看图…
图片中的姑娘是韩国搞笑艺人权尾珍,其实她是一位谐星,体重曾经高达103公斤!后来参加了韩国一个减肥真人秀的节目,用三年时间减重51公斤。瘦下来后,权尾珍通过杂志专栏、电台、博客向大众分享了她的减肥经验,“权式减肥法”也开始风靡韩国。
菲涅尔透镜
你们小时候是不是都拿放大镜照过蚂蚁?!所以你们应该都知道什么叫凸透镜吧?菲涅尔透镜也是一种凸透镜,不过它和一般的凸透镜不一样。它不喜欢普通透镜那种胖胖的身材,所以立志减肥,成功瘦身成了一枚纤细的透镜。至于它为什么叫菲涅尔透镜,跟科学界的其他产物一样,因为它是由法国物理学家奥古斯汀.菲涅尔(Augustin.Fresnel)发明的。那是在1822年,菲涅尔第一次把这种透镜用在了灯塔上。哦,估计也只有用在灯塔上才能体现它身材上的优势。
菲涅尔透镜瘦身成功并不是靠慢跑,也不是靠举铁,更不是靠节食。它主要靠抽脂…科学总是有道理的,我们都知道,光的折射是发生在两种介质相互接触的表面的,(比如玻璃透镜的表面),在一种介质内部是不会发生折射的,所以菲涅尔保留了透镜表面的弯曲度,把里面的东西掏空,把鼓鼓的透镜压扁,变成菲涅尔透镜。理论上是像下面这样:
按照这个理论做出来的菲涅尔透镜是这样的:
很瘦,但依然聚光。
有承诺,没肚子
小姑娘要减肥大家已经司空见惯了。可是你看,连一块透镜都知道要瘦身,我们怎么还好意思不锻炼呢?你可能会说我们码农天天加班没时间,那么问题来了。既然你把时间都用在了代码上,怎么还好意思让自己的代码里到处都是挺着大肚子的Callback调用链呢?不就是要按顺序执行几个异步计算吗?有必要非把自己代码的肚子搞大吗?作为一名负责任的码农,该采取措施还是要采取措施的,把Promise用起来,去掉Pyramid of Doom,还代码一个平坦的小腹,好不好?
Promise
Promise是专门给异步计算用的对象,很早之前就作为神技在几大门派间流传,ES6之后被纳入官库。也就是说现在可以像下面这样直接在代码中定义:
new Promise(function(resolve,reject) {
console.log("Start");
window.setTimeout(function() {
resolve();
}, 2000);
console.log("Waitting");
}).then(function() {
console.log("Finished!");
});
把上面的代码复制到Chrome开发者工具的控制台中执行,得到的结果是这样的:
上面的代码看起来还不太直观,我们来分解一下:
/**
* 执行异步操作的函数,2秒后调用
* 它的回调函数resolve
*/
function asyncMission(resolve) {
window.setTimeout(function() {
resolve();
}, 2000);
}
/**
* 发起异步操作的函数
* @param {function} resolve
* 异步操作成功时调用的回调函数
* @param {function} reject
* 异步操作失败时调用的回调函数
*/
var executor = function(resolve,reject) {
console.log("Start");
asyncMission(resolve);
console.log("Waitting");
}
/**
* 创建一个Promise对象,参数为发起
* 异步操作的那个函数executor
*/
var promiseMission = new Promise(executor);
/**
* 神秘的resolve终于现身了!
* 你可以把方法then的第一个参数
* 当作resolve
*/
promiseMission.then(function() {
console.log("Finished!");
});
看完上面这段代码,再来看看MDN上对Promise的定义:
The Promise object is used for asynchronous computations. A Promise represents an operation that hasn’t completed yet, but is expected in the future.
看不懂英文没关系,这不正好我在嘛!给你翻译一下,用中国话说,定义一个Promise
就相当于老大跟你说:“你出趟远门,老子有个异步计算的差事要交给你办,事成之后,________________"。看到没,给了张空白支票!!!而then
就是让你填那个空的方法,事成之后你想干什么,告诉then
就成了。
不过你应该知道的,老大都是很有原则的人,不会像我上面写的代码一样,只告诉你事成之后可以怎么样。说完好处,他的脸上一定还会浮现出讳莫如深的、蛋蛋的忧伤,告诉你办不成应该怎样。所以一个完整的Promise
应该是这样的:
var p1 = new Promise(
function(resolve, reject) {
window.setTimeout(
function() {
resolve(Math.random());
}, 2000);
}
);
p1.then(
// resolve,val就是上面那个Math.random()的值
function(val) {
console.log(val);
})
.catch(
// reject
function(reason) {
console.log('搞砸了,',reason);
});
Promise一直都知道,在经过漫长的pending之后,事情总会有settled的时候。但settled的结果,有可能是fulfilled,也有可能是rejected。所以我们可以用then
告诉它事成之后怎么办,也可以用catch
告诉它失败了怎么办。关于Promise
,我要说的这么多;不过关于then
,还有很多话要说。
then(what)?
then
是减掉大肚子的关键,看清了then
是什么,就算是掌握了Promise的奥义。来,请看MDN中对then的定义:
The then() method returns a Promise. It takes two arguments: callback functions for the success and failure cases of the Promise.
所有的秘密都在第一句话里…
在前面所有的代码里,都隐藏着一个很容易被忽视的事实,then
也是有返回值的,而且它返回的就是Promise
!不要忘了,我们看到的那个函数只是它的参数,不管它的参数有没有返回值,then
都会返回一个Promise
。我们可以简单地把then的实现理解成下面这个样子:
then(resolve,reject) {
var val = resolve();
return new Promise(
function(_resolve,_reject) {
_resolve(val)
});
}
所以整个故事大概是这样的:开始创建Promise的时候,我们只知道它的参数是一个函数,而这个函数的参数是两个回调函数。这两个回调函数一个是在Promise被fulfilled时调用的resolve,一个是Promise被rejected时调用的reject;但这两个回调函数具体长什么我们并不知道。然后then
登场了,它的参数就是那个神秘的resolve回调函数。哦,对,then
还可以用第二个参数指出reject是谁,但那是2B码农的写法,优雅的程序猿轻易不会露出那么急赤白脸的吃相。reject应该作为catch
的参数出现。
既然then
返回的是Promise
,那then
(和catch
)之后就可以接着then
(和catch
),然后再then
(和catch
),这样callback的调用就可以从回调函数里提出来,放到then
中去,回调函数的调用链就变成平坦的了。
当然,我们都知道,健身不光能让体型好看,还有很多额外的好处,比如血压血脂胆固醇什么的。用Promise写代码也有很多额外的好处,我就不说了,留着你自己慢慢体会吧。
最后,为了感谢你有看这么长时间的耐心,整点干货。
PyramidOfDoom VS chainedThen
憋说话,看代码:
function mission(duration,callback) {
console.log(`Mission ${duration/1000} Start at ${Date.now()}`);
window.setTimeout(function() {
console.log(`Waitting Mission ${duration/1000}`);
callback(duration);
}, duration);
}
(function pyramidOfDoom() {
mission(1000,function() {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
mission(2000,function() {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
mission(3000,function() {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
mission(4000,function() {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
mission(5000,function() {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
mission(6000,function() {
console.log("All missions Completed!");
})
})
})
})
})
});
})();
再看这个:
function promiseMission(duration) {
console.log(`Mission ${duration/1000} Start at ${Date.now()}`);
return new Promise(function(resolve,reject) {
window.setTimeout(function() {
resolve(duration);
console.log(`Waitting Mission ${duration/1000}`);
}, duration);
});
}
promiseMission(1000)
.then(function(duration) {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
return missionPromise(duration+1000);
})
.then(function(duration) {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
return missionPromise(duration+1000);
})
.then(function(duration) {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
return missionPromise(duration+1000);
})
.then(function(duration) {
console.log(`Mission ${duration/1000} Complete at ${Date.now()} after ${duration}`);
return missionPromise(duration+1000);
})
.then(function() {
console.log("All missions Completed!");
})
最后再安利一个教程,优达学院有个免费的JavaScript Promise课程可以看看。还有我的公众号,也可以关注一下:
then then then then then then then then then then then then …
写起来也是像拉。。。,wtf
文章走心 支持一下
@kentkwan 感谢支持
觉得promise的好处,关键是控制的反转,错误处理好过一些
@fundon 是觉得callback用起来更爽?还是搞不懂promise所以很排斥?
@fundon 好像很有道理的样子
promise 是一种设计模式,他不应该只用在针对异步的简单处理
只使用它来处理异步问题,会导致可读性越来越差(滥用)
像这种代码,用 co.js + yield 更合适
@wuhaixing 面对复杂业务逻辑,promise 也不是银弹。
有理有据,令人信服
@fundon 这个说得确实是太有道理了