前端基础进阶之Promise

前言

Promise的重要性我认为我没有必要多讲,归纳起来讲就是必须得掌握,并且还要掌握透彻。这篇文章的开头,主要跟你们分析一下,为何会有Promise出现。ajax

在实际的使用当中,有很是多的应用场景咱们不能当即知道应该如何继续往下执行。最重要也是最主要的一个场景就是ajax请求。通俗来讲,因为网速的不一样,可能你获得返回值的时间也是不一样的,这个时候咱们就须要等待,结果出来了以后才知道怎么样继续下去。数组

复制代码
// 简单的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);
    }
}
复制代码

在ajax的原生实现中,利用了onreadystatechange事件,当该事件触发而且符合必定条件时,才能拿到咱们想要的数据,以后咱们才能开始处理数据。浏览器

这样作看上去并无什么麻烦,可是若是这个时候,咱们还须要作另一个ajax请求,这个新的ajax请求的其中一个参数,得从上一个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() {
            ...
        }
    }
}
复制代码

当出现第三个ajax(甚至更多)仍然依赖上一个请求的时候,咱们的代码就变成了一场灾难。这场灾难,每每也被称为回调地狱函数

所以咱们须要一个叫作Promise的东西,来解决这个问题。url

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

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

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

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

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

fn(want);
复制代码

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

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

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

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

fn(want);
复制代码

若是浏览器已经支持了原生的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的基础知识。

1、 Promise对象有三种状态,他们分别是:

  • pending: 等待中,或者进行中,表示尚未获得结果
  • resolved(Fulfilled): 已经完成,表示获得了咱们想要的结果,能够继续往下执行
  • rejected: 也表示获得结果,可是因为结果并不是咱们所愿,所以拒绝执行

这三种状态不受外界影响,并且状态只能从pending改变为resolved或者rejected,而且不可逆。在Promise对象的构造函数中,将一个函数做为第一个参数。而这个函数,就是用来处理Promise的状态变化。

new Promise(function(resolve, reject) {
    if(true) { resolve() };
    if(false) { reject() };
})

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

2、 Promise对象中的then方法,能够接收构造函数中处理的状态变化,并分别对应执行。then方法有2个参数,第一个函数接收resolved状态的执行,第二个参数接收reject状态的执行。

复制代码
function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    }).then(function() {
        console.log('参数是一个number值');
    }, function() {
        console.log('参数不是一个number值');
    })
}

fn('hahha');
fn(1234);
复制代码

then方法的执行结果也会返回一个Promise对象。所以咱们能够进行then的链式执行,这也是解决回调地狱的主要方式。

复制代码
function fn(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve();
        } else {
            reject();
        }
    })
    .then(function() {
        console.log('参数是一个number值');
    })
    .then(null, function() {
        console.log('参数不是一个number值');
    })
}

fn('hahha');
fn(1234);
复制代码

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

3、Promise中的数据传递

你们自行从下面的例子中领悟吧。

复制代码
var fn = function(num) {
    return new Promise(function(resolve, reject) {
        if (typeof num == 'number') {
            resolve(num);
        } else {
            reject('TypeError');
        }
    })
}

fn(2).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: 2
second: 3
third: 4
复制代码

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最经常使用的一个场景,所以咱们必定要很是很是熟悉它,这样才能在应用的时候更加灵活。

4、Promise.all

当有一个ajax请求,它的参数须要另外2个甚至更多请求都有返回结果以后才能肯定,那么这个时候,就须要用到Promise.all来帮助咱们应对这个场景。

Promise.all接收一个Promise对象组成的数组做为参数,当这个数组全部的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。

复制代码
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var url1 = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-03-26/2017-06-10';

function renderAll() {
    return Promise.all([getJSON(url), getJSON(url1)]);
}

renderAll().then(function(value) {
    // 建议你们在浏览器中看看这里的value值
    console.log(value);
})
复制代码

 

5、Promise.race

与Promise.all类似的是,Promise.race都是以一个Promise对象组成的数组做为参数,不一样的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就能够调用.then方法了。而传递给then方法的值也会有所不一样,你们能够再浏览器中运行下面的例子与上面的例子进行对比。

复制代码
function renderRace() {
    return Promise.race([getJSON(url), getJSON(url1)]);
}

renderRace().then(function(value) {
    console.log(value);
})
复制代码

嗯,我所知道的,关于Promise的基础知识就这些了,若是还有别的,欢迎你们补充。

相关文章
相关标签/搜索