HTML、CSS及JavaScript : 有Promise,不会搞大肚子

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 课程可以看看。

还有我的公众号,也可以关注一下:

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章