Promise是专门给异步计算用的对象,它表示一个现在还没结果,但将来会给你算出来结果来的操作。
憋说话,看图....
图片中的姑娘是韩国搞笑艺人权尾珍,其实她是一位谐星,体重曾经高达103公斤!后来参加了韩国一个减肥真人秀的节目,用三年时间减重51公斤。瘦下来后,权尾珍通过杂志专栏、电台、博客向大众分享了她的减肥经验,“权式减肥法”也开始风靡韩国。
你们小时候是不是都拿放大镜照过蚂蚁?!所以你们应该都知道什么叫凸透镜吧?菲涅尔透镜也是一种凸透镜,不过它和一般的凸透镜不一样。它不喜欢普通透镜那种胖胖的身材,所以立志减肥,成功瘦身成了一枚纤细的透镜。至于它为什么叫菲涅尔透镜,跟科学界的其他产物一样,因为它是由法国物理学家奥古斯汀.菲涅尔(Augustin.Fresnel)发明的。那是在1822年,菲涅尔第一次把这种透镜用在了灯塔上。哦,估计也只有用在灯塔上才能体现它身材上的优势。
菲涅尔瘦身成功并不是靠慢跑,也不是靠举铁,更不是靠节食。它主要靠抽脂......科学总是有道理的,我们都知道,光的折射是发生在两种介质相互接触的表面的,(比如玻璃透镜的表面),在一种介质内部是不会发生折射的,所以菲涅尔保留了透镜表面的弯曲度,把里面的东西掏空,把鼓鼓的透镜压扁,变成菲涅尔透镜。理论上是像下面这样:
按照这个理论做出来的菲涅尔透镜是这样的:
很瘦,但依然聚光。
小姑娘要减肥大家已经司空见惯了。可是你看,连一块透镜都知道要瘦身,我们怎么还好意思不锻炼呢?你可能会说我们码农天天加班没时间,那么问题来了。既然你把时间都用在了代码上,怎么还好意思让自己的代码里到处都是挺着大肚子的Callback调用链呢?不就是要按顺序执行几个异步计算吗?有必要非把自己代码的肚子搞大吗?作为一名负责任的码农,该采取措施还是要采取措施的,把Promise用起来,去掉Pyramid of Doom,还代码一个平坦的小腹,好不好?
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
是减掉大肚子的关键,看清了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写代码也有很多额外的好处,我就不说了,留着你自己慢慢体会吧。
最后,为了感谢你有看这么长时间的耐心,整点干货。
憋说话,看代码:
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 课程可以看看。
还有我的公众号,也可以关注一下: