promise

原文地址https://www.jianshu.com/p/fe5f173276bd
是解决回调地狱的问题
回调地狱
当出现第三个ajax(甚至更多)仍然依赖上一个请求的时候,我们的代码就变成了一场灾难。这场灾难,往往也被称为回调地狱。

var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var result;

var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();

XHR.onreadystatechange = function() {
    if (XHR.readyState == 4 && XHR.status == 200) {
        result = XHR.response;
        console.log(result);

        // 伪代码
        var url2 = 'http:xxx.yyy.com/zzz?ddd=' + result.someParams;
        var XHR2 = new XMLHttpRequest();
        XHR2.open('GET', url, true);
        XHR2.send();
        XHR2.onreadystatechange = function() {
            ...
        }
    }
}

因此 我们需要一个叫promise来解决这个问题

当然,除了回调地狱之外,还有一个非常重要的需求:为了我们的代码更加具有可读性和可维护性,我们需要将数据请求与数据处理明确的区分开来。上面的写法,是完全没有区分开,当数据变得复杂时,也许我们自己都无法轻松维护自己的代码了。这也是模块化过程中,必须要掌握的一个重要技能,请一定重视。

从前面几篇文中的知识我们可以知道,当我们想要确保某代码在谁谁之后执行时,我们可以利用函数调用栈,将我们想要执行的代码放入回调函数中。

// 一个简单的封装
function want() {
   console.log('这是你想要执行的代码');
}

function fn(want) {
   console.log('这里表示执行了一大堆各种代码');

   // 其他代码执行完毕,最后执行回调函数
   want && want();
}

fn(want);

利用回调函数封装,是我们在初学JavaScript时常常会使用的技能。

确保我们想要的代码压后执行,除了利用函数调用栈的执行顺序之外,我们还可以利用上一篇文章所述的队列机制。

function want() {
    console.log('这是你想要执行的代码');
}

function fn(want) {
    // 将想要执行的代码放入队列中,根据事件循环的机制,我们就不用非得将它放到最后面了,由你自由选择
    want && setTimeout(want, 0);
    console.log('这里表示执行了一大堆各种代码');
}

fn(want);

作者:这波能反杀
链接:https://www.jianshu.com/p/fe5f173276bd
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

如果浏览器已经支持了原生的Promise对象,那么我们就知道,浏览器的js引擎里已经有了Promise队列,这样就可以利用Promise将任务放在它的队列中去。

function want() {
    console.log('这是你想要执行的代码');
}

function fn(want) {
    console.log('这里表示执行了一大堆各种代码');

    // 返回Promise对象
    return new Promise(function(resolve, reject) {
        if (typeof want == 'function') {
            resolve(want);
        } else {
            reject('TypeError: '+ want +'不是一个函数')
        }
    })
}

fn(want).then(function(want) {
    want();
})

fn('1234').catch(function(err) {
    console.log(err);
})


看上去变得更加复杂了。可是代码变得更加健壮,处理了错误输入的情况。

为了更好的往下扩展Promise的应用,这里需要先跟大家介绍一下Promsie的基础知识。

一、Promise的三种状态

pending 等待中 或者进行中 表示还没有得到结果
resolved 已经完成,表示得到了我们想要的结果,可以继续往下执行
rejected 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行
这三种状态不受外界影响而且状态只能从pending到resolved或者rejected,不可逆。在Promise对象的构造函数中,将一个函数作为第一个参数,这个函数的用来处理Promise的状态变化的

new Promise(function(){
if(true){ resolved() }
if(false){ rejected() }
})

上面的resolve和reject都为一个函数,他们的作用分别是将状态修改为resolved和rejected。

二、Promise对象的 then 方法

Promise对象的 then 方法,可以接收构造函数中处理的状态变化,并分别对应执行。then 方法有两个参数,第一个函数接收 resolved 状态的执行,第二个参数接收 rejected 状态的执行。

then(null, function(){}) 就等同于 catch(function(){})

三、Promise的数据传递

var fnn = function(num){
    return new Promise(function (resolve,reject) {
      if(typeof num == 'number'){
        resolve(num)
      }else{
        reject('TypeError')
      }
    })
  }


  fnn(1).then(function (num) {
    console.log('first: ' + num)
    return num + 1;
  })
    .then(function (num) {
      console.log('second: ' + num)
      return num + 1;
    })
    .then(function (num) {
      console.log('third: ' + num)
      return num + 1;
    })
//输出结果
first: 1
second:2
third:3

OK,了解了这些基础知识之后,我们再回过头,利用Promise的知识,对最开始的ajax的例子进行一个简单的封装。看看会是什么样子。

var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';

// 封装一个get请求的方法
function getJSON(url) {
   return new Promise(function(resolve, reject) {
       var XHR = new XMLHttpRequest();
       XHR.open('GET', url, true);
       XHR.send();

       XHR.onreadystatechange = function() {
           if (XHR.readyState == 4) {
               if (XHR.status == 200) {
                   try {
                       var response = JSON.parse(XHR.responseText);
                       resolve(response);
                   } catch (e) {
                       reject(e);
                   }
               } else {
                   reject(new Error(XHR.statusText));
               }
           }
       }
   })
}

getJSON(url).then(resp => console.log(resp));

为了健壮性,处理了很多可能出现的异常,总之,就是正确的返回结果,就resolve一下,错误的返回结果,就reject一下。并且利用上面的参数传递的方式,将正确结果或者错误信息通过他们的参数传递出来。

现在所有的库几乎都将ajax请求利用Promise进行了封装,因此我们在使用jQuery等库中的ajax请求时,都可以利用Promise来让我们的代码更加优雅和简单。这也是Promise最常用的一个场景,因此我们一定要非常非常熟悉它,这样才能在应用的时候更加灵活。

四、Promise.all
当有一个ajax请求,它的参数需要另外2个甚至更多请求都有返回结果之后才能确定,那么这个时候,就需要用到Promise.all来帮助我们应对这个场景。
Promise.all接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。

 var p1 = new Promise(function (resolve, reject) {
   setTimeout(resolve, 500, 'P1');
 });
 var p2 = new Promise(function (resolve, reject) {
   setTimeout(resolve, 600, 'P2');
 });
 // 同时执行p1和p2,并在它们都完成后执行then:
 Promise.all([p1, p2]).then(function (results) {
   console.log(results); // 获得一个Array: ['P1', 'P2']
 });

五、Promise.race
与promise.all相似的是 promise.race都是以一个Promise对象组成的数组作为参数,不同的是当数组中的其中一个Promise状态变成resolved 或者rejected 时,就可以调用.then方法了。而传递给then方法的值也会有所不同。大家可以在浏览器中运行

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

嗯,我所知道的,关于Promise的基础知识就这些了,如果还有别的,欢迎大家补充。

那么接下来,我们要结合三个不同的应用场景来让大家感受一下Promise在模块系统中如何使用。

这里选择require.js是因为学习成本低,能够快速上手进行简单的运用。

catch用法

catch的用法

看到这个方法,就会想到浏览器处理异常的try...catch()方法,有错误进入catch方法,不阻断程序的执行,其实这个方法也是来处理错误的,用来指定reject的回调,防止程序错误,阻断后面程序的执行,使其能够继续执行。

getNumber()
           .then(function(data){
              console.log('resolve');
              console.log(data);
            })
            .catch(function(data){
               console.log('reject');
               console.log(data);
             })

其效果和上面在then里面写两个函数是一样的,这个写法的好处是当代码出现了错误的时候,不会阻断程序的执行,而是进入catch方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342