• ES6中的Promise使用方法与总结


    在javascript中,代码是单线程执行的,对于一些比较耗时的IO操作,都是通过异步回调函数来实现的。

    但是这样会存在一个问题,当下一个的操作需要上一个操作的结果时,我们只能把代码嵌到上一个操作的回调函数里,这样一层嵌一层,最终形成回调地狱。

    $.get('/login.php', function (login) {
        $.get('/user.php', function (user) {
            $.get('/info.php', function (info) {
                //代码就这样一层嵌一层,不够直观,维护也麻烦
            });
        });
    });
    

    为了解决这种问题,ES6中就提供了Promise方法来解决这种问题。

    Promise是一个构造函数,通过它,我们可以创建一个Promise实例对象。

    let p = new Promise(function (resolve, reject) {
        setTimeout(() => {
            console.log('OK');
            resolve('OK');
        }, 1000);
    });
    

    Promise构造函数接受一个函数作为参数,这个函数有两个参数,resolve和reject。

    resolve函数是将Promise的状态设置为fulfilled(完成),reject函数是将Promise的状态设置为rejected(失败)。

    上述代码,我们并没有进行任何调用,当运行时,间隔1秒后输出了'OK'。所以这里需要注意,我们通常使用Promise时,需要在外层再包裹一层函数。

    let p = function () {
        return new Promise(function (resolve, reject) {
            setTimeout(() => {
                console.log('OK');
                resolve('OK');
            }, 1000);
        });
    };
    
    p();
    

    上面的代码p();返回的是一个Promise实例对象,Promise对象上有 then() , catch() , finally() 方法。

    then方法有两个参数,onFulfilled和onRejected,都是函数。

    onFulfilled用于接收resolve方法传递过来的数据,onRejected用于接收reject方法传递过来的数据。

    let p = function () {
        return new Promise(function (resolve, reject) {
            setTimeout(() => {
                if (Math.random() > 0.5) {
                    resolve('OK');
                } else {
                    reject('ERR');
                }
            }, 1000);
        });
    };
    
    p().then(function (data) {
        console.log('fulfilled', data);
    }, function (err) {
        console.log('rejected', err);
    });
    

    then()方法总是会返回一个Promise实例,这样我们就可以一直调用then()。

    在then方法中,你既可以return 一个具体的值 ,还可以return 一个Promise对象。

    如果直接return的是一个数据,那then方法会返回一个新Promise对象,并以该数据进行resolve。

    let p = function () {
        return new Promise(function (resolve, reject) {
            resolve(1);
        });
    };
    
    p().then(function (data) {
        console.log(`第 ${data} 次调用`);
        //注意这里直接返回的值
        //then会创建一个新的Promise对象,并且以返回的值进行resolve
        //那么该值会被下面的then方法的onFulfilled回调拿到
        return ++data;
    }).then(function (data) {
        console.log(`第 ${data} 次调用`);
        return ++data;
    }).then(function (data) {
        console.log(`第 ${data} 次调用`);
        return ++data;
    });
    

    如果返回的是一个Promise对象,请看下面代码。

    let p = function () {
        return new Promise(function (resolve, reject) {
            resolve(1);
        });
    };
    
    p().then(function (data) {
        console.log(`第 ${data} 次调用`);
    
        return new Promise(function (resolve, reject) {
            resolve(++data);
        });
    }).then(function (data) {
        console.log(`第 ${data} 次调用`);
    
        return new Promise(function (resolve, reject) {
            resolve(++data);
        });
    }).then(function (data) {
        console.log(`第 ${data} 次调用`);
    
        return new Promise(function (resolve, reject) {
            resolve(++data);
        });
    });
    

    其实效果与直接返回值的是一样的。

    即然then()可以进行链式操作,那我们最早之前的回调地狱写法,就可以通过它进行改进了。

    function login() {
        return new Promise(function (resolve, reject) {
            $.get('/login.php', function (result) {
                resolve(result);
            });
        });
    }
    
    function user(data) {
        return new Promise(function (resolve, reject) {
            $.get('/user.php', function (result) {
                resolve(result);
            });
        });
    }
    
    function info(data) {
        return new Promise(function (resolve, reject) {
            $.get('/info.php', function (result) {
                resolve(result);
            });
        });
    }
    
    login().then(function (data) {
        console.log('处理login');
        //把login异步处理获取的数据,传入到下一个处理中。
        return user(data);
    }).then(function (data) {
        console.log('处理user');
        //把user异步处理获取的数据,传入到下一个处理中。
        return info(data);
    }).then(function (data) {
        console.log('处理info');
    });
    

    这样修改后,回调地狱层层嵌套的结构就变的清晰多了。上述代码是伪代码。

    Promise对象还有一个catch方法,用于捕获错误,该方法与 then(null, onRejected) 等同,是一个语法糖。

    let p = function () {
        return new Promise(function (resolve, reject) {
            resolve('开始');
        });
    };
    
    p().then(function (data) {
        console.log('1');
        return new Promise(function (resolve, reject) {
            reject('错误1');
        });
    }).then(function (data) {
        console.log('2');
        return new Promise(function (resolve, reject) {
            reject('错误2');
        });
    }).then(function (data) {
        console.log('3');
        return new Promise(function (resolve, reject) {
            reject('错误3');
        });
    }).catch(function (reason) {
        console.log(reason);
    });
    

    注意,一旦操作中有错误发生,则会进入到catch中,后面的操作将不再执行。

    Promise对象内部自带了try catch,当代码运行时错误,会自动以错误对象为值reject,并最终被catch捕获。

    let p = function () {
        return new Promise(function (resolve, reject) {
            resolve('开始');
        });
    };
    
    p().then(function (data) {
        //注意这里打印了一个未定义的变量
        console.log(a);
    }).catch(function (reason) {
        //这里会捕获到错误
        console.log('rejected');
        console.log(reason);
    });
    

    Promise还提供了,all(),race(),reject(),resolve()等在构造函数上的方法,调用这些方法并不需要实例化对象。

    all()方法,可以让我们并行的执行异步操作,直到所有操作完成了,才执行回调。

    function fn1() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn1');
            }, 1000);
        });
    }
    
    function fn2() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn2');
            }, 2000);
        });
    }
    
    function fn3() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn3');
            }, 3000);
        });
    }
    
    //all会等待所有操作完成,会把所有操作的结果放到一个数组中,传给then。
    Promise.all([fn1(), fn2(), fn3()]).then(function (data) {
        console.log(data);
    });
    

    race()方法是谁先处理完,就以谁为准,把最先处理完的结果传给then。

    function fn1() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn1');
            }, 1000);
        });
    }
    
    function fn2() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn2');
            }, 2000);
        });
    }
    
    function fn3() {
        return new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('fn3');
            }, 3000);
        });
    }
    
    //race是以谁先处理完,就以谁为准,fn1最先处理完,那fn1的结果会传给then
    //注意这里fn2和fn3还是会执行,不过不会进入then了
    Promise.race([fn1(), fn2(), fn3()]).then(function (data) {
        console.log(data);
    });
    

    reject()方法,返回一个带有拒绝原因reason参数的Promise对象。

    // Promise.reject('错误') 
    // 等同于
    // new Promise(function(resolve, reject) {
    //     reject('错误');
    // });
    let p = Promise.reject('错误');
    
    p.then(function (data) {
    
    }).catch(function (reason) {
        console.log(reason);
    });

    resolve()方法,根据传入的值返回一个Promise对象。

    //如果传入的参数是普通值,则返回一个新Promise对象,并以该值resolve
    let p1 = Promise.resolve('OK');
    p1.then(function (data) {
        console.log(data);
    });
    
    //如果传入的参数是一个Promise对象,则原封不动的返回该Promise对象
    let obj = new Promise(function (resolve, reject) {
        resolve('我是Promise对象');
    });
    let p2 = Promise.resolve(obj);
    p2.then(function (data) {
        console.log(data);
        console.log(p2 === obj);
    });
    
    //如果传入的参数是一个thenable对象(带有then方法),
    //会转换成Promise对象,并执行thenable对象的then方法
    let then = {
        then(resolve, reject) {
            resolve('我是thenable对象');
        }
    }
    let p3 = Promise.resolve(then);
    p3.then(function (data) {
        console.log(data);
    });
    
    //如果什么参数都不传入,则返回状态为resolved的Promise对象
    let p4 = Promise.resolve();
    p4.then(function (data) {
        console.log(data);
    }).catch(function (reason) {
        console.log(reason);
    });
    

      

  • 相关阅读:
    洛谷1113 杂务
    MySQL中的各种引擎
    剑指offer第3题:从尾到头打印链表
    向一个GitHub repository添加协作者
    String、StringBuffer与StringBuilder之间区别
    java与C语言在字符串结束符上的区别
    git 查看远程分支、本地分支、创建分支、把分支推到远程repository、删除本地分支
    Git问题Everything up-to-date解决
    Mybatis 数据库物理分页插件 PageHelper
    时间序列分析发展史
  • 原文地址:https://www.cnblogs.com/jkko123/p/10217356.html
Copyright © 2020-2023  润新知