为了账号安全,请及时绑定邮箱和手机立即绑定

ES6之Promise

标签:
React.JS React

什么是Promise

Promise是es6中提供的一个构造函数,是异步编程的一种解决方案。

为什么要用promise,在es6以前,又是怎么解决异步编程的呢?

首先,我们需要知道js是以单线程运行的,所以像网络操作、浏览器事件等都是异步执行的。

异步指的是发出一个功能调用之后,这个调用就直接返回了,所以没有返回结果。当这个调用完成后,一般通过状态、通知和回调来通知调用者。es6之前是使用了回调的方式通知调用者。现在也可以使用promise进行处理了。

假设现在有一个需求,先根据用户的id获取到用户的角色ID,然后根据用户的角色id,查询此用户能访问到的商品列表。

// ajax函数

function mockAjax(method, url, data, success, error) {

    setTimeout(() => {

        console.log('异步请求’);

        var timeOut = Math.random() * 10;

        if (timeOut < 5) {

            const result = {status: 100, msg: ’success’, data: []}

            success(result);

        }

        else {

            error('接口地址错误');

        }

    },1000);

}


function getList(userId) {

    mockAjax(‘GET', ‘/user/role?userId=' + userId, function success(data) {

     mockAjax(‘GET', '/product/list?roleId=' + data.roleId, function success(data) {

        // 逻辑处理

    }, function error(error) {

    // 错误处理

    });

}, function error(error) {

    // 错误处理    

});

}

可以看到,这段代码的可读性不是很好,我们的例子只是嵌套了2层,如果是更多的嵌套又会是怎么样的呢?

promise主要是为了解决js中多个异步回调难以维护和控制的问题。

那么我们应该怎么用Promise函数呢?

我们可以在chrome浏览器上的控制台打印console.dir(Promise),查看Promise的具体组成。

https://img1.sycdn.imooc.com//5e9d394300014e8d16240586.jpg

从打印信息上可以看得出Promise其实是一个构造函数,Promise函数上还有有all,race, resolve,reject等静态方法

Promise函数上的prototype属性上有then,catch、finally方法,因此只要是Promise的实例,都可以调用Promise.prototype上面的方法(then,catch)。


既然是构造函数,那么我们就可以使用new操作符创建一个promise对象

let promise = new Promise();

在实例化Promise时需要传入一个执行函数作为参数,并且在创建对象时同步执行

let promise = new Promise(() => {

    var timer = setTimeout(() => {

        console.log('异步请求');

             },1000);

         });


运行这段代码,1s后,我们可以在控制台上看到输出的结果,这就说明在实例化过程中,作为参数的函数也会执行

执行函数实际上还有两个参数resolve和reject,其实这两个参数也是函数,下面我们学习一下resolve和reject的用法

let promise = new Promise((resolve, reject) => {

    var timer = setTimeout(() => {

        console.log('异步请求');

    },1000);

});


我们先来学习resolve的用法

首先我们来看看Promise的几种状态:

        pending: 初始状态,成功或失败状态。

        fulfilled: 意味着操作成功完成。

        rejected: 意味着操作失败。


       当我们在执行函数中调用resolve方法时,Promise的状态就变成fulfilled,即操作成功状态,还记得上面Promise.prototype上面的then和catch方法吗?当Promise状态为fullfilled状态时执行then方法里的操作,注意了,then方法里面有两个参数onfulfilled(Promise为fulfilled状态时执行) 和onrejected(Promise为rejected状态时执行),步骤如下:

1,实例化Promise(new Promise(function(resolve,reject)))

2,用Promise的实例调用then方法

let promise = new Promise((resolve, reject) => {

    setTimeout(() => {

        console.log('异步请求’);

        const result = {status: 100, msg: ’success’, data: []}

        resolve(result)

    },1000);

});

promise.then(data => {

    console.log(data);

})

在这个小例子中,在Promise的执行函数中的setTimeout1秒之后,执行resolve函数,这个promise的状态变成了fulfilled,然后执行了then方法里的第一个函数,也就是onfulfilled函数。其实then里面的函数就是我们平时所说的回调函数,只不过Promise将执行代码和处理结果的代码分离开来了。

那么通过上面的小例子,reject应该也就能很好的理解了,就是调用reject方法后,Promise状态变为rejected,即操作失败状态,此时执行then方法里面onrejected操作,也就是then方法中的第二个函数

let promise = new Promise((resolve, reject) => {

    var timeOut = Math.random() * 2;

    setTimeout(() => {

        console.log('异步请求’);

        if (timeOut < 1) {

            const result = {status: 100, msg: ’success’, data: []}

            resolve(result)

        }

        else {

            reject(‘error');

        }

    },1000);

});

promise.then(data => {

    console.log(data);

}, error => {

    console.log(error);

})


之前我们打印Promise时,它的prototype属性、也就是原型上还有catch函数,这个函数又是做什么的呢?

catch方法的作用和then方法的第二个参数onrejected回调函数是一样的,当promise的状态变成了rejected时,就执行catch方法,实际上catch方法就是then方法的一个语法糖

let promise = new Promise((resolve, reject) => {

    var timeOut = Math.random() * 2;

    setTimeout(() => {

        console.log('异步请求’);

        if (timeOut < 1) {

            const result = {status: 100, msg: ’success’, data: []}

            resolve(result)

        }

        else {

            reject('接口地址错误');

        }

    },1000);

});

promise.then(data => {

    console.log(data);

})

promise.catch(error => {

    console.log(error);

})

then函数也可以链式调用

promise.then(data => {

    console.log(data);

}).catch(error => {

    console.log(error);

})

promise原型上还有一个finally方法

在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。

promise.then(data => {

    console.log(data);

}).catch(error => {

    console.log(error);

}).finally(() => {

    console.log('统一处理')

});



简单的Promise就讲完了,现在我们可以使用promise修改例子

// ajax函数将返回Promise对象:

function mockAjax(method, url, data) {

    return new Promise(function (resolve, reject) {

        var timeOut = Math.random() * 10;

    setTimeout(() => {

        console.log('异步请求', timeOut);

        if (timeOut < 5) {

            const result = {status: 100, msg: 'success', data: []}

            resolve(result)

        } else {

            reject('接口地址错误');

        }

    },1000);

    });

}

function getList(userId) {

    mockAjax('GET', '/role?userId=' + userId)

    .then(data => {

        return mockAjax('GET', '/product/list?roleId=' + data.roleId);

    })

    .then(data => {

        console.log('商品列表', data);

    }).catch(error) => {

        console.log('error', error);

    });

}

getList(1);

从代码量上看,使用promise好像更多了,但是promise最大的优势就是将执行过程代码和结果处理的代码解藕了。增强了代码的可读性和可维护性。


上面这个例子是一个串行执行的任务,只有执行完第一个任务之后,才可以执行第二个任务。


有的时候,也需要并行执行异步任务,我们可以通过Promise的静态方法all来实现,比如说在一个页面需要同时展示用户信息和角色信息,并且需要等这两个接口都返回了值,才进行渲染

let getUser = mockAjax('GET', '/user?userId=' + 1);

let getRole = mockAjax('GET', '/role?userId=' + 1);

// 同时执行getUser和getRole,并在它们都完成后执行then:

Promise.all[getUser, getRole].then(results => {

    console.log(‘results’, results);

}).catch(error => {

    console.log(‘error’, error);

});


// Promise.all可以将多个Promise实例包装成一个新的Promise实例。成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

还有的时候,我们同时发起多个请求,只需要获得先返回的结果即可,另外的请求都会停止。我们可以通过Promise.race静态方法写一个支持超时的异步请求。,race意思就是竞赛的意思

// ajax函数将返回Promise对象:

function mockAjax(method, url, data) {

    return new Promise(function (resolve, reject) {

        var timeOut = Math.random() * 10;

    setTimeout(() => {

        console.log('异步请求', timeOut);

        if (timeOut < 5) {

            const result = {status: 100, msg: 'success', data: []}

            resolve(result)

        } else {

            reject('接口地址错误');

        }

    },1000);

    });

}

function delay(ms = 500) {

    return new Promise((resolve, reject) => {

        setTimeout(resolve, ms)

    })

}

function timeoutAjax(promise) {

     var timeout = delay().then(function () {

            throw new Error('请求超时');

        });

    return Promise.race([promise, timeout]);

}

let getUser = mockAjax('GET', '/user?userId=' + 1);

timeoutAjax(getUser).then(data => {

    console.log('data', data);

}).catch(error => {

    console.log('error', error)

})





点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
Web前端工程师
手记
粉丝
104
获赞与收藏
33

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消