ES6(Promise)

2020-04-29  本文已影响0人  KATENGC

Promise是异步编程的解决方案。举个例子,有一个函数A,A执行一些步骤,A执行完要执行函数B,这里有个顺序问题,就是A执行完执行B。那在程序上如何实现这个场景呢?
这里有两种方式,一种是通过回调的方式实现,另外一种是通过事件触发的方式实现。而Promise是区别与以上两种方式的。

一、Promise的基本用法

先来回顾下ES5中利用回调来解决异步的问题
我们模拟一个前端与服务端通信时的ajax过程,添加一个callback回调方法,用于接收服务端的返回结果。这里用定时器setTimeout模拟了一个通信的过程,一秒钟之后会调用回调方法。

{
    let ajax = function (callback) {
        console.log('执行');
        setTimeout(() => {
            callback && callback.call();
        }, 1000); 
    };

    ajax(function () {
        console.log('timeout1');
    })
}

先输出了执行,一秒钟之后执行了回调函数,输出了timeout1,这样就实现了一个异步操作。

但是开发过程中经常会遇到比较复杂的情况,先执行A再执行B,甚至执行完B再执行C,如果用ES5中回调的方式去处理,这么这个代码将非常复杂。除此之外,这个代码的复杂还会影响后期的代码维护,无法一眼看出代码执行的顺序问题,很难阅读,所以呢,为了解决这些问题,就引出了Promise这个解决方案。

函数执行完之后会返回一个对象,这个对象就是Promise的实例,这个实例有一个then方法去执行下一步的功能。这里会有两个参数resolverejectresolve表示要执行下一步操作,而reject表示中断当前的操作

{
    let ajax = function () {
        console.log('执行2');
        /**
         * resolve:要执行下一步的操作
         * reject:要中断当前的操作
         */
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve();
            }, 1000);
        })
    };

    ajax().then(() => {
        console.log('promise', 'timeout2');
    });
}

执行结果:
先打印执行2,一秒钟之后再打印出timeout2

那么对于执行完函数A再执行函数B的场景,利用Promise就很容易实现了

{
    let ajax = function () {
        console.log('执行3');
        
        return new Promise(function (resolve, reject) {

            setTimeout(function () {
                resolve();
            }, 1000);
        })
    };

    ajax()
        .then(() => {
            return new Promise(function (resolve, reject) {
                console.log('执行3-1');
                setTimeout(function () {
                    resolve();
                }, 2000);
            })
        })
        .then(() => {
            console.log('timeout3');
        });
}

上面的代码就模拟了两个函数的执行过程。输出执行3,一秒后打印出执行3-1,再过两秒后输出timeout3

可以清楚看到利用Promise实现的优势,多个函数执行只要再前一个Promisethen方法中再new一个Promise,同时再添加then用于接收下一个函数的执行结果。整个结构非常清晰,代码可维护。
但是这边还要考虑一个问题,如果在这个串行过程中(A==>B==>C),其中某个函数执行出现了异常,那如何来捕获这个错误?幸运的是Promise已经提供了这样一个方法catch,可以用来捕获异常错误。

{
    let ajax = function (num) {
        console.log('执行4', num);
        /**
         * resolve:要执行下一步的操作
         * reject:要终止当前的操作
         */
        return new Promise((resolve, reject) => {
            if (num > 5) {
                resolve();
            } else {
                throw  new Error('出错啦');
            }
        })
    };

    ajax(6).then(() => {
        console.log('log', 6);
    }).catch(err => {
        console.log('catch', err);
    });

    //修改传入的num值,num小于5就会抛出错误,catch能捕捉到该错误
    // ajax(2).then(() => {
    //     console.log('log', 6);
    // }).catch(err => {
    //     console.log('catch', err);
    // })
}
image.png
二、Promise的高级用法
{
    // 所有图片加载完(忽略加载成功或加载失败)再添加到页面
    function loadImg(src) {
        return new Promise(((resolve, reject) => {
            let img = document.createElement('img');
            img.src = src;
            img.onload = function () {
                resolve(img);
            };

            img.onerror = function (err) {
                reject(err);
            }
        }))
    }

    function showImgs(imgs) {
        imgs.forEach(img => {
            document.body.appendChild(img);
        })
    }

    //Promise.all 将多个Promise实例当作一个Promise实例,当所有Promise实例的状态发生改变时,这个Promise实例才会跟着发生变化
    //在这里也就是说,只有当三个图片的所有的状态都完成之后,才会触发Promise.all这个新的Promise
    Promise.all([
        loadImg('https://pics6.baidu.com/feed/e4dde71190ef76c6f8a619195cd869fcae5167e5.jpeg?token=83461bdb4a26ce68441d12a04ce4c127&s=843053975A4226CC5F84691E0300C063'),
        loadImg('https://pics2.baidu.com/feed/77094b36acaf2edd362ad591288595ef3801930f.jpeg?token=939c0e1cf863beba12e9e0dfa3f42b23'),
        loadImg('https://pics7.baidu.com/feed/64380cd7912397dd701ca9865b2226b1d1a28775.jpeg?token=9df8d6fb97b587e7131aee048d334c6a')
    ]).then(showImgs)
}
{

    // 任意一张图片加载完就添加到页面
    function loadImg(src) {
        return new Promise(((resolve, reject) => {
            let img = document.createElement('img');
            img.src = src;
            img.onload = function () {
                resolve(img);
            };

            img.onerror = function (err) {
                reject(err);
            }
        }))
    }

    function showImgs(img) {
        let p = document.createElement('p');
        p.appendChild(img);
        document.body.appendChild(p);

    }

    //Promise.race 三个Promise中任何一个Promise的状态发生改变,这个Promise就会发生改变
    //在这里也就是说,三张图片任一张图片加载完就显示在页面上,先到先得
    Promise.race([
        loadImg('https://pics6.baidu.com/feed/e4dde71190ef76c6f8a619195cd869fcae5167e5.jpeg?token=83461bdb4a26ce68441d12a04ce4c127&s=843053975A4226CC5F84691E0300C063'),
        loadImg('https://pics2.baidu.com/feed/77094b36acaf2edd362ad591288595ef3801930f.jpeg?token=939c0e1cf863beba12e9e0dfa3f42b23'),
        loadImg('https://pics7.baidu.com/feed/64380cd7912397dd701ca9865b2226b1d1a28775.jpeg?token=9df8d6fb97b587e7131aee048d334c6a')
    ]).then(showImgs)
}
上一篇下一篇

猜你喜欢

热点阅读