zoukankan      html  css  js  c++  java
  • Promise的一些相关讲解

    在javascrpit的语言特性上 有明确的一个特性指出,该语言的是单线程进程。这就意味着JavaScript的所有网络操作,浏览器事件,都必须是异步执行。

    如下面的例子,可以感受到单线程与异步回调:

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

    将上面代码在控制台打出 我们可以看的到

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

    可以看出,异步操作 会在未来的某个点 由回调函数触发

    再来看一个典型的异步操作:ajax请求

    // 这个是原生的ajax请求一部分  注册事件 onreadystatechange 状态改变就会调用
    request.onreadystatechange = function () {
        if (request.readyState === 4) { //判断服务器是否响应正确
            if (request.status === 200) {
                return success(request.responseText);
            } else {
                return fail(request.status);
            }
        }
    }
    // 另外 附上原生的ajax请求
    //步骤一:创建异步对象
    var ajax = new XMLHttpRequest();
    //步骤二:设置请求的url参数,参数一是请求的类型,参数二是请求的url,可以带参数,动态的传递参数starName到服务端
    ajax.open('get','getStar.php?starName='+name);
    //步骤三:发送请求
    ajax.send();
    //步骤四:注册事件 onreadystatechange 状态改变就会调用
    ajax.onreadystatechange = function () {
    if (ajax.readyState==4 &&ajax.status==200) {
    //步骤五 如果能够进到这个判断 说明 数据 完美的回来了,并且请求的页面是存在的
        console.log(ajax.responseText);//输入相应的内容
      }
    }

    对于上面的代码 有些繁琐,可以改写嘛?比如这样

    var ajax = ajaxGet('http://...');
    ajax.ifSuccess(success)
        .ifFail(fail);

     上面的代码 体现了一种链式写法,这种写法看起来比较简洁些,并且还体现了另外一个思想:

    我不管你有没有请求成功,但是我发起了与服务器的通信,至于结果,我可以再在未来的某个代码片段里去调用 success 与 fail 的函数。

    于是 这种 " 承诺于未来将会执行 " 的对象,被称为了promise  中文含义 : 承诺。

    promise 有很多的开源实现 在ES6中被统一规范,由浏览器直接支持。可以测试你的浏览器是否支持promise。在控制台上打印以下代码:

    new Promise(function () {
       console.log('我支持promise') 
    })

     既然知道了promise的大概含义,我们来实现一个这样的例子吧:下面代码里 我们默认当num<1时 是通信成功,不然就是通信失败。模拟一个ajax请求。

            function test(success,fail) {
                var num = Math.random() * 2;
                console.log('相应时间为'+ num +'秒')
                setTimeout(function () {
                    if(num < 1) {
                        success('执行成功函数');
                    }else {
                        fail('执行失败的函数')
                    }
                },num * 1000)
            }

    能够看出 , test()函数有两个参数,其实就是回调函数,当num < 1时 我们定义其为相应成功,调用success函数,失败时 就调用 fail 函数。由此可以看出:

    test()函数只在关心自身的逻辑,并没有去关心 执行success 还是 fail 函数 如何去处理结果。

    于是 我们可以用promise对象 来出来这个执行的函数。

            var p1 = new Promise(test);
            var p2 = p1.then(function (data) {
                console.log('成功' + data)
            });
            var p3 = p2.catch(function (data2) {
                console.log('失败' + data2)
            })

    变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,我们告诉Promise对象:

    如果执行成功 则执行

    p1.then(function (data) {
                console.log('成功' + data)
            });

    失败 则执行:

    p2.catch(function (data2) {
                console.log('失败' + data2)
            })

    Promise对象可以串联起来,所以上述代码可以简化为:

    new Promise(test).then(function (data) {
        console.log('成功:' + data);
    }).catch(function (data2) {
        console.log('失败:' + data2);
    });

    继续上面的栗子 看看promise是如何实现异步执行的:

    new Promise(function (success, fail) {
        console.log('start new Promise...');
        var timeOut = Math.random() * 2;
        console.log('set timeout to: ' + timeOut + ' seconds.');
        setTimeout(function () {
            if (timeOut < 1) {
                console.log('执行成功函数');
                success('200 OK');
            }
            else {
                console.log('执行失败函数');
                fail('timeout in ' + timeOut + ' seconds.');
            }
        }, timeOut * 1000);
    }).then(function (r) {
        console.log('Done: ' + r);
    }).catch(function (reason) {
        console.log('Failed: ' + reason);
    });

    可以将上面的代码打入控制台  你可以看到类如下面的结果

    可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了

    此时 我们再看一个axios的请求  axios支持promise API

    axios.get('/user?ID=12345')
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });

    怎么样 形式上 是不是就会感觉到 一种很熟悉的,这样也就能理解 axios请求的写法了。

    Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

    要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

    job1.then(job2).then(job3).catch(handleError);

    下面有个具体的例子说明:

    // 0.5秒后返回input*input的计算结果:
    function multiply(input) {
        return new Promise(function (resolve, reject) {
            console.log('计算 ' + input + ' x ' + input + '...');
            setTimeout(resolve, 500, input * input);
        });
    }
    
    // 0.5秒后返回input+input的计算结果:
    function add(input) {
        return new Promise(function (resolve, reject) {
            console.log('计算 ' + input + ' + ' + input + '...');
            setTimeout(resolve, 500, input + input);
        });
    }
    
    var p = new Promise(function (resolve, reject) {
        console.log('开始promise');
        resolve(123);
    });
    
    p.then(multiply)
     .then(add)
     .then(multiply)
     .then(add)
     .then(function (result) {
        console.log('最后的结果 ' + result);
    });
    View Code

    在控制台打出上面的代码 可以看到;

    我们可以再看一下 vuejs 官方提供的与服务器通信的插件 axios 的大概实现原理。这里是基于原生ajax请求改写

    function ajax(method,url,params) {
                    var request = new XMLHttpRequest();
                    return new Promise(function (success,fail) {
                        request.onreadystatechange = function () {
                            if(request.readyState === 4 && request.status === 200) {
                                success(request.responseText);
                            }else {
                                fail(request.status)
                            }
                        }
                        request.open(method,url);
                        request.send(params)
                    })
                }

    紧接着 我们的调用

    var p = ajax('GET', '/api',params);
    p.then(function (text) { // 如果AJAX成功,获得响应内容
        console.log(text)
    }).catch(function (status) { // 如果AJAX失败,获得响应代码
        lconsole.log(status);
    });

    另外:

    promise.all()方法,可以支持并行异步操作

    var p1 = new Promise(function (resolve, reject) {
                    setTimeout(resolve, 500, '第一个');
                });
                var p2 = new Promise(function (resolve, reject) {
                    setTimeout(resolve, 600, '第二个');
                });
                // 同时执行p1和p2,并在它们都完成后执行then:
                Promise.all([p1, p2]).then(function (results) {
                    console.log(results); // 获得一个Array: ['第一个', '第二个']
                });

    Promise.race()方法,可以实现两个异步请求,有一个先返回即可

                var p1 = new Promise(function (resolve, reject) {
                    setTimeout(resolve, 500, '第一个');
                });
                var p2 = new Promise(function (resolve, reject) {
                    setTimeout(resolve, 600, '第二个');
                });
                // 同时执行p1和p2,并在它们都完成后执行then:
                Promise.race([p1, p2]).then(function (results) {
                    console.log(results); // 输出第一个 第二个会被默认丢弃
                });
                

    由于p1运行较快,所以promise的then 会获得 ‘第一个’的输出。第二个会被默认丢弃。

    综上:

    如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行 

  • 相关阅读:
    在ubuntu下复制文件出现权限不够的解决方法
    Ubuntu安装ROS Melodic
    gedit文件操作
    Linux下强制删除文件和权限操作
    VMware Tools 继续运行脚本未能在虚拟机中成功运行 解决方式
    Linux解压命令
    Ubuntu 18.04.4 LTS(Bionic Beaver)安装
    Socket层实现系列 — send()类发送函数的实现
    iOS7 CookBook精彩瞬间(三)UIActivityViewController的基本使用及自定义Activity
    iOS7 CookBook精彩瞬间(二)NSSet、通过Subscript访问类成员等
  • 原文地址:https://www.cnblogs.com/mamimi/p/9446594.html
Copyright © 2011-2022 走看看