zoukankan      html  css  js  c++  java
  • Promise

    一、Promise 相关概念

    学习参考 廖雪峰 的博客:

    在JavaScript的世界中,所有代码都是单线程执行的。

    由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行,异步执行可以用回调函数实现:

    function callback() {
        console.log('Done');
    }
    console.log('before setTimeout()');
    setTimeout(callback, 1000); // 1秒钟后调用callback函数
    console.log('after setTimeout()');
    

    观察上述代码执行,在Chrome的控制台输出可以看到:

    before setTimeout()
    after setTimeout()
    (等待1秒后)
    Done
    

    即:异步操作会在将来的某个时间点触发一个函数调用。

    再来一个例子:

    回调函数使用频率很高的不能不提 ajax 请求,由于网速等问题,客户端发起一个ajax请求后,得到响应的时间是不固定的。

    在ajax的原生实现中,利用了onreadystatechange事件,当该事件触发并且符合一定条件时,才能拿到我们想要的数据,之后我们才能开始处理数据。

    var  xhr = new XMLHttpRequest();
    xhr.open('get','http://localhost:3000/api/one',true);
    xhr.send();
    xhr.onreadystatechange = function () {
        console.log(xhr.readyState);
        if(xhr.readyState === 4){  // 代表服务器已经给了响应, 不代表响应成功
            if(xhr.status === 200){
                console.log(xhr.response);
            }
        }
    }
    
    

    如果这个时候,我们还需要做另外一个ajax请求,这个新的ajax请求中的一个参数,得从上一个ajax请求中获取,这个时候,得在上一个里面继续进行一个 ajax 请求:

    var xhr = new XMLHttpRequest();
    xhr.open('get','http://localhost:3000/api/one',true);
    xhr.send();
    xhr.onreadystatechange = function () {
        console.log(xhr.readyState);
        if(xhr.readyState === 4){  // 代表服务器已经给了响应, 不代表响应成功
            if(xhr.status === 200){
               // 在这里进行新的 ajax 请求
                var xhr2 = new XMLHttpRequest();
                xhr2.open('get','http://localhost:3000/api/one',true);
                xhr2.send();
                xhr.onreadystatechange = function () {.........}
            }
        }
    }
    

    好,那么如果再来一个呢,这个就是 回调地狱了,为了解决 回调地狱 的问题,让代码更加可读,于是有了 Promise

    “君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。



    二、Promise 原理

    Promise对象有三种状态:

    • pending: 等待中,或者进行中,表示还没有得到结果
    • resolved(Fulfilled): 已经完成,表示得到了我们想要的结果,可以继续往下执行
    • rejected: 也表示得到结果,但是由于结果并非我们所愿,因此拒绝执行

    三种状态不受外界影响,状态只能从 pending 变为 resolved 或者 rejected.

    function fn(num){
        return new Promise(function (resolve,reject) {
            if(typeof num == 'number'){
                resolve();
            }else{
                reject();
            }
        }).then(function () {
            console.log('It is a number');
        },function () {
            console.log('It is not a number');
        })
    }
    
    fn(1);
    fn('hello');
    // 打印结果:
    /*
    It is a number
    It is not a number
    */
    
    • resolve() 和 reject() 的作用就是将状态修改为 resolved 和 rejected

    • .then() 里面两个 function() 参数,第一个是满足条件的resolve,第二个是不满足条件的 reject.分别根据获取到的状态去执行相应的函数


    then 方法执行完后会返回一个 promise对象,所以可以进行.then的链式调用,这样就解决了回调地狱的问题

    function fn(num) {
        return new Promise(function (resolve,reject) {
            if(typeof num == 'number'){
                resolve();
            }else{
                reject();
            }
        })
        .then(function () {
            console.log('It is a number');
        })
        .then(null,function () {
            console.log('It is not a number');
        })
    }
    
    fn(123);
    fn('hello');
    /*
    执行结果:
    It is a number
    It is not a number
    */
    
    



    三、关于Promise的数据传递

    var fn = function (num) {
        return new Promise(function (resolve,reject) {
            if(typeof num == 'number'){
                resolve(num)
            }else {
                reject('not a number')
            }
        })
    }
    
    
    fn(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
    * */
    

    多个参数传递:

    new Promise((resolve, reject)=>{
        resolve(1, 2, 232, 212);
    }).then((data1, data2, data3, data4)=>{
        console.log(data1, data2, data3, data4);
    });
    



    四、错误处理

    关于错误处理:在使用 Promise 做异步流程控制的时候,关于异常的处理可以通过在最后一个 then 之后设置一个 catch,然后指定一个失败处理函数, 该函数可以捕获前面所有的 Promise 对象本身以及 then 内部的任务错误,当前面任何一个发生异常,直接进入 catch,后续所有的 Promise 包括 then 不再执行

    const fs = require('fs');
    
    // 1. 创建一个Promise对象 (一经创建, 立马执行)
    readFile(__dirname + '/data/a.txt', 'utf8')
        .then((data) => {
            console.log(data.toString());
            return readFile(__dirname + '/data/b.txt', 'utf8');
        })
        .then((data) => {
            console.log(data.toString());
            return readFile(__dirname + '/data/c.txt', 'utf8');
        })
        .then((data) => {
            console.log(data.toString());
        }).catch(err => {
            console.log(err);
    });
    
    
    function readFile(...args) {
        return new Promise((resolve, reject) => {
            fs.readFile(...args, (err, data) => {
                if (err) {
                    reject(err);
                }
                resolve(data);
            })
        })
    }
    



    五、封装ajax

    主流框架的ajax一般都是 用Promise封装的。

    Promise 封装ajax 示例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <script>
        function xhr(options) {
            return new Promise(function (resolve, reject) {
                var xhr = new XMLHttpRequest();
    
                xhr.open(options.type || 'get', options.url,true);
                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(xhr.responseText);
                        }
    
                    }
                };
                xhr.send();
            })
        }
    
        xhr({
            url:'http://ip.taobao.com/service/getIpInfo.php?ip=117.89.35.58',
            type:'get',
            data:''
        }).then((data)=>{
            console.log(data);
        },function (err) {
            console.log(err);
        })
    
    
    </script>
    </body>
    </html>
    

    Promise封装ajax,待补充一个完整的,上面的这个现在只处理了 get 方式的


    六、Promise.all

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

    var p1 = new Promise(function (resolve, reject) {
        resolve('111');
    });
    
    var p2 = new Promise(function (resolve,reject) {
        resolve('222');
    });
    
    var p3 = new Promise(function (resolve,reject) {
       reject('333')
    });
    
    Promise.all([p1,p2]).then(function (data) {
        console.log(data);    // 打印 [ '111', '222' ]
    }).catch(function (err) {
        console.log(err);
    });
    
    Promise.all([p1,p2,p3]).then(function (data) {
        console.log(data);
    }).catch(function (err) {
        console.log(err);  // 打印:333
    })
    
    • Promise.all 得到所有成功的结果的顺序,与传入的Promise对象的数组顺序保持一致。如上面的代码,即使是p2的结果先出来,顺序也不会变。
    • 如果我们下一步的动作,需要等待其他的两个结果先出来才能判读是否要往下进行,用Promise.all 来解决。

    七、Promise.race

    race :种族,赛跑

    Promise.race([p1,p2,p3]), 里面的三个任务赛跑,谁先获取到结果谁就跑的快,不管返回的结果是成功还是失败。

    let p1 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('1000秒--success');
            console.log(1111);
        },1000)
    })
    
    let p2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('500秒--failed')
            console.log(2222);
        }, 500)
    })
    
    
    Promise.race([p1, p2]).then((result) => {
        console.log('resolve:'+result)
    },function (err) {
        console.log('reject:'+err);
    }).catch((error) => {
        console.log('catch:'+error)  // 打开的是 'failed'
    });
    
    /*
    执行结果:
    2222
    reject:500秒--failed
    1111
    * */
    
    • 只要是在数据组里的,都会执行
    • 只返回了跑得快的任务的结果
  • 相关阅读:
    含有tuple的list按照tuple中的某一位进行排序
    python
    Python代码追踪(类似于bash -x的效果)
    cinder swift的区别
    C#中Main函数为什么要static
    C#编程.函数.委托
    C#编程.函数.Main()函数
    C#编程.函数.参数
    typedef int a[10];怎么解释?
    C#的DateTime得到特定日期
  • 原文地址:https://www.cnblogs.com/friday69/p/10402795.html
Copyright © 2011-2022 走看看