zoukankan      html  css  js  c++  java
  • 从零开始学习前端JAVASCRIPT — 13、Ajax-前后端异步交互以及Promise-异步编程的改进

    (注:本章讲解涉及部分后端知识,将以php提供数据的方式进行相应的demo实现)

    一、ajax的概念

    全称:Asynchronous Javascript And Xml

    AJAX不是一种新的编程语言,而是一种用于创建更快更好以及交互性更强的WEB应用程序技术,该技术在98年前后得到了应用。通过AJAX,你的JS可以通过JS的XMLHttpRequest对象在页面不重载的情况下与服务器直接进行通信。这样可以在服务器请求到想要的数据,而不是整个页面。AJAX的核心就是JS的XMLHttpRequest对象。xhr对象是在IE5中首次引入,它是一种支持异步请求的对象。

    二、ajax的优势

    • 无刷新更新数据。
    • 异步与服务器通信。
    • 基于标准被广泛支持。
    • 前端与后端分离。
    • 节省带宽。

    三、编写步骤

    1.创建XMLHttpRequest对象。

    所有现代浏览器(IE7+,chrome,firefox,opera,safari)均内建XMLHttpRequest对象。但是IE5、6使用ActiveXObject对象。

    function getAjax() {
    
    var  xmlhttp = null;
    
    if(window.ActiveXObject){
    
       //针对IE
    
             xmlhttp = new ActiveXObject(’Microsoft.XMLHTTP’);
    
    } else if(window.XMLHttpRequest){
    
             xmlhttp = new XMLHttpRequest();
    
    }
    
    return  xmlhttp;
    
    }

    2.打开与Server的连接,指定发送方式、URL以及权限等。

    open方法:创建新的HTTP请求,并指定此请求的方法,URL以及验证信息。

    xhr.open(type, url, async, user, password);

    type:HTTP请求方式,GET、POST等。大小写不敏感。

    url:请求地址。(get请求如果有传值直接以url?param=value的方式传递。post的方式直接在发送指令的时候传递)

    async:布尔型,请求是否为异步方式。默认为true。如果为真,当状态改变时会调用onreadystatechange属性指定的回调函数。(可选)

    注释:当您使用 async=false 时,请不要编写 onreadystatechange 函数 - 把代码放到 send() 语句后面即可:

    xmlhttp.open("GET","test.txt",false);
    xmlhttp.send();
    document.getElementById("myDiv").innerHTML=xmlhttp.responseText;

         user:如果服务器需要验证,此处指定用户名,如果未指定,当服务器需要验证时,会弹出验证窗口。少用仅了解

    password:验证信息中的密码部分,如果用户名为空,则此值将会被忽略。(少用仅了解)

    注:在AJAX中,其实我们就是来模拟正常的表单提交数据。正常的表单在POST数据时,会发送Content-Type字段,所以我们在AJAX中就要指定该字段值为application/x-www-form-urlencoded。并且对字段名称和值进行编码处理在发送。使用setRequestHeader:单独指定请求的某个HTTP头。

    注:一些特殊字符可能与代码中的字符冲突(如URL中分隔符&等等),故数据应使用encocdeURIComponent()函数进行编码。

    3.发送指令。

    send():发送请求到HTTP服务器并接收回应。

    此方法的同步或异步方式取决于open方法中的async参数,如果async为true,此方法将立即返回,如果为false,此方法将会等待请求完成或者超时时才会返回。

    xhr.send(body);

    body:通过此请求发送的数据。GET请求设置为null即可。

    post和get方式实现ajax的不同之处 

    xmlhttp.open("GET","demo_get2.asp?fname=Bill&lname=Gates",true);
    xmlhttp.send();
    
    xmlhttp.open("POST","ajax_test.asp",true);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.send("fname=Bill&lname=Gates");
    
    //传参方式的不同以及编码方式的不同post提交需要被别,则需要指定编码方式与get的方式一样

       4.等待并接收服务器返回的处理结果。

    5.客户端接收。

    6.释放XMLHttpRequest对象。

    四、回调函数

    通过onreadystatechange属性指定readystate属性改变时的事件处理回调函数。

    xhr.onreadystatechange = function(){}

    readyState属性:返回请求的当前状态。

    • 0:对象已建立,尚未初始化(未调用open方法)。
    • 1:对象已建立,尚未调用send方法。
    • 2:send方法已调用。但是当前的状态以及HTTP状态未知。
    • 3:开始接收数据,因为响应以及HTTP头不全,这时通过responseBody和responseText获取部分数据会出现错误。
    • 4:数据接收完毕,此时可以通过responseBody和responseText获取完整的响应数据。

    status属性:返回当前请求的状态码。

    • 200 OK:请求文档已经找到,并正确返回。
    • 304 Not Modified:拥有一个本地的缓存副本,服务器端内容与此相同。
    • 403 Forbidden:请求者对所请求的文档不具有相应的权限。
    • 404 Not Found:请求的文档没找到。

    statusText属性:返回当前请求的响应行信息。

    responseXML属性:将响应信息格式化为XML Document对象返回。

    responseText属性:将响应信息作为字符串返回。

     

    五、JS解析JSON 

     JSON:Javascript Object Notation,一种轻量级的基于文本的数据交换格式,易于人阅读和编写,也能提高网络传输速率。

     ES5新增的两个方法:

     JSON.parse:将JSON字符串数据转换为JSON对象。

     JSON.stringify:将JSON对象转换为JSON字符串。

     注:1、浏览器支持:IE8+。

      2、JSON格式的字符串里面的key或者字符串型的value都必须用双引号包裹

    六、局部数据刷新 

    demo讲解上述提到的知识点:操作相应的DOM节点(例如评论列表的分页效果)

    html页面

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>新闻列表</title>
        <style type="text/css">
            *{
                margin:0;
                padding:0;
            }
            .box{
                 80%;
                margin: 0 auto;
            }
            .newsTitle{
                font-size: 24px;
                text-align: center;
                margin: 0 auto;
                padding: 20px 10px;
                font-weight: normal;
                letter-spacing: 10px;
            }
            .newsList{
                display: block;
                margin: 0 auto;
                line-height: 50px;
                font-size: 16px;
                list-style: none;
            }
            .newsList li{
                border-top: 1px dashed #f4f4f4;
                padding: 0 20px;
            }
            .newsList li:nth-child(odd){
                background: #f3f3f3;
            }
            .newsPage{
                display: block;
                margin: 0 auto;
                text-align: center;
            }
            .newsPage li{
                margin:20px 5px;
                display: inline-block;
                padding: 5px  10px;
                border:1px solid #f3f3f3;
                font-size: 14px;
            }
        </style>
    </head>
    <body>
        <div class="box">
            <h3 class="newsTitle">新闻列表</h3>
            <ul id="newsList" class="newsList">
                <li>东航首位外籍女机长:来中国是最好的选择</li>
                <li>东航首位外籍女机长:来中国是最好的选择</li>
                <li>东航首位外籍女机长:来中国是最好的选择</li>
                <li>东航首位外籍女机长:来中国是最好的选择</li>
                <li>东航首位外籍女机长:来中国是最好的选择</li>
            </ul>    
            <ul id="newsPage" class="newsPage">
                <li data-page="1">1</li>
                <li data-page="2">2</li>
                <li data-page="3">3</li>
                <li data-page="4">4</li>
                <li data-page="5">5</li>
                <li data-page="6">6</li>
                <li data-page="7">7</li>
                <li data-page="8">8</li>
                <li data-page="9">9</li>
                <li data-page="10">10</li>
            </ul>        
        </div>
    </body>
    <script type="text/javascript" src="ajax.js"></script>
    </html>

     js页面

    var oNewsPage=document.getElementById('newsPage');
    var oNewsPage=Array.from(oNewsPage.children);
    var oNewsList=document.getElementById('newsList');
    
    oNewsPage.forEach(function (value) {
        value.onclick=function () {
            oNewsList.innerHTML = "";
            //获取页码值
            var pageNum = value.getAttribute("data-page");
    
            /*步骤一:创建XMLHttpRequest对象*/
            var xhr = new XMLHttpRequest();
    
            /*步骤二:请求配置*/
            var pageNum = "pageNum="+ pageNum;
            //xhr.open('get','ajax.php?'+pageNum,true);
            xhr.open('post','ajax.php',true);
    
            /*步骤四:接受返回结果*/
            xhr.onreadystatechange=function () {
                //根据状态返回码判定请求的状态
                if (xhr.readyState === 4 && xhr.status === 200) {
                    //获取返回结果
                    var newsList = JSON.parse(xhr.responseText)//返回结果通过json将字符串进行转换
                    //数据结果渲染到页面
                    newsList.forEach( v => {
                        var oLi = document.createElement('li');
                        oLi.innerHTML = v.title;
                        oNewsList.appendChild(oLi);
                    });
                }
            }
    
            /*步骤三:发送请求*/
            //post请求需要设置编码格式  否则无法解析数据
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(pageNum);//post方式将参数传递的方式
        }
    })

     php后台页面

    <?php
        $pageNum=$_POST['pageNum']; //接收参数页码
        //通过页码判断加载的内容
        if ($pageNum == 1) {
            //通过二维数组存储数据
            $arr = [
                [
                    'id' => 1,
                    'title' => '东航首位外籍女机长:来中国是最好的选择'
                ],
                [
                    'id' => 2,
                    'title' => '南航首位外籍女机长:来中国是最好的选择'
                ],
                [
                    'id' => 3,
                    'title' => '国际航空首位外籍女机长:来中国是最好的选择'
                ]
            ];        
        }
        else if ($pageNum == 2) {
            $arr=[
                [
                    'id' =>4,
                    'title'=>'世界那么大不如出去走走'
                ],
                [
                    'id'=>5,
                    'title'=>'今天阳光明媚适合出去走走'
                ]
            ];
        }
        else{        
            $arr=[
                [
                    'id' =>6,
                    'title'=>'我是第三页以后的内容'
                ],
                [
                    'id'=>7,
                    'title'=>'我是第三页以后的内容'
                ]
            ];
        }
        echo json_encode($arr);
    ?>

    七、前后端分离(ajax函数的封装)

     后台只管数据输出和业务逻辑处理,前端负责交互逻辑和界面展示。简单的说:前端静态页面中没有有后台程序代码,后台输出不带有HTML标签的数据。前后端分离靠ajax来实现数据的交互。

    demo通过封装的ajax实现加载更多的功能以及通过事件源对象删除单条数据(本来打算将源码托管到GitHub,账号丢失)

    *文件夹目录 

     1.html页面

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>新闻列表</title>
        <link rel="stylesheet" href="styles/ajax.css">
    </head>
    <body>
        <div class="box">
            <h3 class="newsTitle">新闻列表</h3>
            <ul id="newsList" class="newsList">
                <li>
                    冬天来了,春天还会远么 
                    <a class="deleteBtn" href="#">&times;</a>
                </li>
                <li>
                    面朝大海,幸福花开 
                    <a class="deleteBtn" href="#">&times;</a>
                </li>
                <li>
                    Nothing is impossible! 
                    <a class="deleteBtn" href="#">&times;</a>
                </li>
                <li>
                    水能载舟亦能覆舟 
                    <a class="deleteBtn" href="#">&times;</a>
                </li>
                <li>
                    中国是最好的选择 
                    <a class="deleteBtn" href="#">&times;</a>
                </li>
            </ul>    
            <div id="loadMoreInfo" class="loadMoreInfo">
                <img id="loadingImg" class="loading" src="images/timg.gif" />
                加载更多...
            </div>    
        </div>
    </body>
    <script type="text/javascript" src="scripts/common.js"></script>
    <script type="text/javascript" src="scripts/ajax.js"></script>
    </html>
    ajax.html

     2.css文件

    @charset "utf-8";
    /* CSS Document */
    *{
        margin:0;
        padding:0;
    }
    body{
        background: #7CCD7C;
    }
    .box{
         80%;
        margin: 0 auto;
    }
    .newsTitle{
        font-size: 24px;
        text-align: center;
        margin: 0 auto;
        padding: 20px 10px;
        font-weight: normal;
        letter-spacing: 10px;
    }
    .newsList{
        display: block;
        margin: 0 auto;
        line-height: 50px;
        font-size: 16px;
        list-style: none;
    }
    .newsList li{
        padding: 0 20px;
        box-shadow: 0 0 10px #eeeeee;
    }
    .box .newsList li:hover{
        background: #B0E2FF;
    }
    .newsList li:nth-child(odd){
        background: #f3f3f3;
    }
    .newsList li:nth-child(even){
        background: #eeeeee;
    }
    
    .deleteBtn{
        float: right;
        text-decoration: none;
        font-size: 16px;
        color: #555555;
    }
    .loadMoreInfo{
        margin-top: 10px;
        text-align: center;
        line-height: 50px;
        font-size: 16px;
        background: #ffffff;
        cursor: pointer;
        vertical-align: middle;
        margin-bottom: 20px;
    }
    .loadMoreInfo img{
        display: none;
         20px;
        height: 20px;
        vertical-align: middle;
    }
    ajax.css

     3.公共js(封装的$函数以及ajax函数)

    /* 
    封装$函数
    参数说明:
    id:标签的id属性值
    */
    function $(id) {
        return document.getElementById(id);
    }
    
    /*
    封装ajax函数
    参数说明:对象作为参数不用考虑参数顺序问题
    type:请求类型;
    url:请求访问地址;
    param:请求数据,支持对象也支持字符串;
    asyn:是否异步加载,boolean类型,true异步,false同步
    beforesend:回调函数在发送请求之前执行
    success:回调函数,请求成功时执行
    complete:回调函数,请求完成后执行
    */
    function ajax({type,url,param,asyn=true,beforesend,success,complete}) {
        //创建XMLHttpRequest对象
        var xhr = new XMLHttpRequest();
        //param参数为对象时进行拼接字符串
        if (param && typeof(param) === 'object') {
            var str;
            for(var child in param){
                //encodeURIComponent编码参数,避免特殊字符分割时丢失
                str+=encodeURIComponent(child) + '=' + encodeURIComponent(param[child]) + '&'
            }
            param=str.slice(0,-1);//截取拼接完成后最后的&符
        }
        //get请求对url传参进行拼接
        if (type.toUpperCase()==="GET" && param) {
            url+='?'+param;
        }
        //请求配置
        xhr.open(type , url, asyn);
        //获取返回的数据
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && xhr.status === 200) {
                //获取返回的数据并转换
                success(xhr.responseText);
            }
            //请求完成后的函数
            complete && complete();
        }
        //发送请求
        beforesend && beforesend();
        if (type.toUpperCase()==="POST") {
            //post请求设置编码方式
            xhr.setRequestHeader("Content-Type",'application/x-www-form-urlencoded')
            //post请求时通过send向body传参(无论param参数是否为空,必须传)
            xhr.send(param);
        }
        else{
            //get请求发送请求
            xhr.send();
        }
    }
    common.js

     4.页面js

    var oLoadMoreInfo = $("loadMoreInfo");
    var oNewsList = $("newsList");
    var oLoadingImg = $("loadingImg");
    oLoadMoreInfo.onclick=function () {
        ajax({
            type:'post',
            url:'php/ajax.php',
            beforesend:function () {
                //发送前显示加载过程中的动态效果
                oLoadingImg.style.display="inline-block"
            },
            success:function (data) {
                //接受返回的数据进行转换并渲染到页面
                var data=JSON.parse(data);
                data.forEach(function (value) {
                    var li=document.createElement("li");
                    li.innerHTML=value+'<a class="deleteBtn" href="#">&times;</a>';
                    oNewsList.appendChild(li);
                })
            },
            //完成后显示渲染出来的网页效果,关闭等待效果
            complete:function () {
                oLoadingImg.style.display="none"
            }
        })
    }
    newsList.onclick=function (ev) {
        var e=ev||window.event;
        //获取事件原对象
        var tar=e.target||e.srcElement;
        //判断事件原对象是否为a标签nodenName的值都是大写
        if (tar.nodeName==="A") {
            oNewsList.removeChild(tar.parentNode);
        }
    }
    ajax.js

     5.php后台

    <?php
      sleep(2);//本地测试资源加载速度过快无法看出加载的动态效果
      $news=[
          '东航首位外籍女机长:来中国是最好的选择',
          '南航首位外籍女机长:来中国是最好的选择',
          '国际航空首位外籍女机长:来中国是最好的选择',
          '东航首位外籍女机长:来中国是最好的选择',
          '南航首位外籍女机长:来中国是最好的选择',
          '国际航空首位外籍女机长:来中国是最好的选择',
          '东航首位外籍女机长:来中国是最好的选择',
          '南航首位外籍女机长:来中国是最好的选择',
          '国际航空首位外籍女机长:来中国是最好的选择',
          '东航首位外籍女机长:来中国是最好的选择',
          '南航首位外籍女机长:来中国是最好的选择',
          '国际航空首位外籍女机长:来中国是最好的选择'
      ];
      echo json_encode($news, JSON_UNESCAPED_UNICODE);
      /*  PHP的json_encode来处理中文的时候, 中文都会被编码, 变成不可读的, 类似”u***”的格式,PHP5.4,JSON_UNESCAPED_UNICODE:Json不要编码Unicode*/
    ajax.php

     6.图片资源

     

    八、ajax的依赖调用

    在使用ajax异步调用的时候,可能碰到同时调用多个ajax的情况,而且多个ajax之间还存在依赖关系。

    引用回调函数的嵌套说明ajax的以来调用: 

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>回调函数的嵌套</title>
    </head>
    <body>
        
    </body>
    <script type="text/javascript">
        function a(d, e) {
            d && d(e);
        }
    
        function b(f) {
            console.log('函数b!');
            f && f();
        }
    
        a(b, function() {
            console.log('实参e(回调函数)');
        });
    /*回调函数的简单理解:函数在方法中以参数的方式进行传递*/
    /*这里提供两种形式理解回调函数的嵌套*/
    /*    function a(d, e) {
            d && d(e);
        }
    
        a(
            function b(f){
                console.log('函数b!');
                f && f();
            }, 
            function() {
                console.log('实参e(回调函数)');
            }
        );*/
    
    </script>
    </html>
    回调函数的嵌套

     demo:逐个异步验证验证码、用户名和密码,只有用户通过,才能异步验证密码。

     此处只给出部分重要代码逐个调用封装的ajax方法(前后端分离的demo封装的ajax方法)进行异步验证验证码、用户名和密码: 

                // 表单提交事件用纯ajax实现表单验证
                // 验证验证码
                var sCaptcha = oCaptcha.value;
                ajax({
                    type: 'POST',
                    url: 'login.php',
                    data: {captcha: sCaptcha},
                    success: function (data) {
                        if(data == 1) {
                            // 验证账号
                            var sAccount = oAccount.value;
    
                            ajax({
                                type: 'POST',
                                url: 'login.php',
                                data: {account: sAccount},
                                success: function (data) {
                                    if(data == 1) {
                                        // 验证密码
                                        var sPassword = oPassword.value;
                                        ajax({
                                            type: 'POST',
                                            url: 'login.php',
                                            data: {password: sPassword},
                                            success: function (data) {
                                                if(data == 1) {
                                                    oShow.innerHTML = '登录成功!';
                                                } else {
                                                    oShow.innerHTML = '密码错误!';
                                                }
                                            }
                                        });
                                    } else {
                                        oShow.innerHTML = '账号错误!';
                                    }
                                }
                            });
                        } else {
                            oShow.innerHTML = '验证码错误!';
                        }
                    }
                });
    ajax异步调用demo 

    九、Promise介绍

    Promise函数,是一个构造函数,它实际上是对回调函数的一种封装 对异步编程的一种改进。

    1>Promise对象的特点:

    1.Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

    2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

    Promise的优点:

      Promise对象可以将异步操作以同步操作的流程表达出来,避免层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

    Promise的缺点:

    1.无法取消Promise,一旦新建它就会立即执行,无法中途取消。

    2.如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

    3.当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

    2>Promise的基本用法 

    Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。(Promise新建后就会立即执行。)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
            // 创建promise对象(创建后就被执行)
            var oP = new Promise(function (resolve, reject) {
                console.log('立即执行');
                resolve();
                reject();
            });
        </script>
    </body>
    </html>

     

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

    reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    1)Promise的then方法:指定Resolved状态和Reject状态的回调函数。 

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
        // 创建promise对象
        var oP = new Promise(function (resolve, reject) {
            console.log('立即执行');
            // resolve();
            /*状态由pending 变为执行成功,
            调用then函数的第一个回调函数*/
            reject();
            /*想要查看reject对应的then的回调函数,
            可将resolve()函数注释*/
            /*reject函数单独执行异常,将then的第二
            个参数回调函数注释可查看*/
        });
    
        oP.then(function() {
            console.log('第一个');
            //只执行第一个then回调函数(一旦执行状态就无法改变)
        }, function () {
            console.log('第二个');
        });
        </script>
    </body>
    </html>

    then方法可以接受两个回调函数作为参数。 

    第一个回调函数:Promise对象的状态变为Resolved时调用 

    第二个回调函数:Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

    then方法支持链式写法且同时调用

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
            // 创建promise对象
            var oP = new Promise(function (resolve, reject) {
                console.log('立即执行');
                resolve();
                reject();
            });
            // 链式写法
            oP.then(function() {
                console.log('第一个');
            }, function () {
                console.log('第二个');
            }).then(function() {
                console.log('第三个');
            }).then(function () {
                console.log('第四个');
            });
        </script>
    </body>
    </html>

     

    只有被Promise封装过了,回调才能保证顺序。 也就是Promise(意为承诺)设计的初衷。但前一个方法必须在它最后执行resolve(),后一个方法才可以开始。 如果执行了reject(),则进入catch()方法。这里不能单纯的理解为 resolve就是success,reject就是error 现就职于阿里的大名鼎鼎的阮一峰老师,喜欢管它叫状态机,这是非常恰当的叫法。

    2)catch方法:catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法。如果没有使用catch方法指定错误处理的回调函数,Promise对象抛出的错误不会传递到外层代码,即不会有任何反应。

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
            // 创建promise对象
            var oP = new Promise(function (resolve, reject) {
                console.log('立即执行');
                // resolve();
                reject();
            });
            // 链式写法
            oP.then(function() {
                console.log('第一个');
            }, function () {
                console.log('第二个');
            })
            // catch捕获错误
            oP.catch(function () {
                console.log('出错啦!');
            });
            // console.dir(oP);
        </script>
    </body>
    </html>

     

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
            // 创建promise对象
            var oP = function(){
                return new Promise(function (resolve, reject) {
                //报错x未定义,无输出(为捕获异常)
                resolve(x+2);
            })};
            oP().then(function() {
                console.log('执行正确');
            })
        </script>
    </body>
    </html>

    3>promise构造函数身上的方法

     1)Promise.all()Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

     var p = Promise.all([p1,p2,p3]);

    promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise对象的实例。

    p的状态由p1、p2、p3决定,分成两种情况。

    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。 

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
        var p1 = new Promise(function (resolve, reject) {
            console.log('p1');
            resolve();
        });
    
        var p2 = new Promise(function (resolve, reject) {
            console.log('p2');
            reject();
        });
        var p3 = new Promise(function (resolve, reject) {
            console.log('p3');
            resolve();
        });
        var p4 = Promise.all([p1, p2, p3]);
        console.log(p4);//返回值reject
        </script>
    </body>
    </html>

    2)Promise.race()将多个Promise实例,包装成一个新的Promise实例。 

     var p = Promise.race([p1,p2,p3]);

    上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Promise</title>
    </head>
    <body>
        <script>
        var p1 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                console.log('p1');
                resolve();
            }, 400);
        });
    
        var p2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                console.log('p2');
                reject();
            }, 800);
        });
        var p3 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                console.log('p3');
                resolve();
            }, 2000);
        });
        var p4 = Promise.race([p1, p2, p3]);
        /*p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。
        率先改变的 Promise 实例的返回值,就传递给p的回调函数。*/
        console.log(p4);//返回值reject
        </script>
    </body>
    </html>

    4>promise改造ajax依赖调用(以 8:ajax的依赖调用demo改写为例

    文件目录结构:

    1.html页面

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户登录</title>
        <style type="text/css">
            *{
                margin: 0;
                padding: 0;
            }
            html,body{
                height: 100%;
                overflow: hidden;
            }
            body{
                position: relative;
            }
            .login-box{
                position: fixed;
                left: 0;
                right:0;
                top: 0;
                bottom: 0;
                margin: auto;
                 90%;
                background: #f3f3f3;
                height: 90%;
                box-shadow: 0 0 10px #333333,0 0 8px #f3f3f3;
                border: 1px solid #eeeeee
            }
            .loginRegion{
                 50%;
                height: 400px;
                position: absolute;
                left: 0;
                right: 0;
                top: 0;
                bottom: 0;
                margin: auto;
            }
            .inputRegion{
                margin: 10px auto;
                 90%;
                height:60px;
                font-size: 18px;
                border:1px solid #555555;
                background: #ffffff;
            }
            .remindInfo{
                margin: 0 auto 30px;
                 90%;
                height:30px;
                line-height: 30px;
                font-size: 28px;
                background: #f3f3f3;
                color: #000000;
                text-align: center;
                font-weight: bold;
                letter-spacing: 10px;
            }
            .inputRegion label{
                padding: 0 8px;
                font-size: 22px;
                vertical-align: middle;
            }
            .inputRegion input{
                padding: 0;
                margin: 0;
                height: 100%;
                outline: none;
                border:none;
                font-size: 20px;
                 80%;
                vertical-align: middle;
            }
            .btnRegin input{
                 100%;
                border:1px solid #90EE90;
                font-size: 24px;
                text-align: center;
                background: #9AFF9A;
                font-weight: normal;
                color: #ffffff;
            }
        </style>
    </head>
    <body>
        <div class="login-box">
            <form method="post" action="javascript:;" name="login" class="loginRegion">
                <div id="remindInfo" class="remindInfo">
                    用户登录
                </div>
                <div class="inputRegion account">
                    <label>账&nbsp;&nbsp;&nbsp;号</label><input type="text"  name="account" autocomplete="off" />
                </div>    
                <div class="inputRegion password">
                    <label>密&nbsp;&nbsp;&nbsp;码</label><input type="password"  name="password" autocomplete="off"/>
                </div>
                <div class="inputRegion captcha">
                    <label>验证码</label><input type="text"  name="captcha" autocomplete="off"/>
                </div>            
                <div class="inputRegion btnRegin">
                    <input type="submit"  name="submitBtn" value="登&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;录"/>
                </div>
            </form>
        </div>
    </body>
    <script type="text/javascript" src="scripts/common.js"></script>
    <script type="text/javascript" src="scripts/login.js"></script>
    </html>
    login.html

     2.js文件

    1)公共js($函数的封装,promise改写ajax函数的封装)

    /*
    author:MR.Shi
    create date:2018/03/01
    updater:
    update date:
    function name $
    introduce param:
        id:id property value
    */
    function $(id) {
        return document.getElementById(id);
    }
    
    /*
    author:MR.Shi
    create date:2018/03/01
    updater:
    update date:
    function name:ajax
    introduce param:
        type:post/get
        url:post url/get url
        asyn:true/false default:true 
        param:string type or object type
    */
    function ajax({type,url,asyn=true,param,beforesend,complete}) {
        return new Promise(function (resolve,reject) {
            /*创建XMLHttpRequest对象*/
            var xhr = new XMLHttpRequest();
            /*参数为对象时按照get请求时url?传参的格式进行拼接字符串*/
            if (param&&typeof(param)==='object') {
                var sParam = "";
                for(var key in param){
                    sParam += encodeURIComponent(key)+ "=" +encodeURIComponent(param[key]) + "&";
                }
                /*去除字符串最后的&分割符号*/
                param = sParam.slice(0,-1);
            }
            console.log(param)
            /*get请求时url地址拼接参数*/
            if (type.toUpperCase()==="GET" && param) {
                url += '?' + param;
            }
            /*配置请求*/
            xhr.open(type,url,asyn);
            /*在发送请求之前执行的函数*/
            beforesend&&beforesend();
            /*发送请求*/
            if (type.toUpperCase()==="POST") {
                /*设置post请求时编码规则,否则无法解析数据*/
                xhr.setRequestHeader("content-type","application/x-www-form-urlencoded")
                /*post传参时参数封装到body通过send方法发送*/
                xhr.send(param);
            }
            else{
                /*get请求send直接发送*/
                xhr.send();
            }
            /*接收请求返回数据*/
            xhr.onreadystatechange=function () {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    resolve(xhr.responseText)
                }
                if (xhr.status>=400) {
                    reject(new error(xhr.status));
                }
                complete&&complete();
            }        
        });
    }
    common.js

     2)页面js文件(promise的链式调用)

    var oForm=document.login;
    var  oRemindInfo=$("remindInfo");
    oForm.onsubmit=function () {
        ajax({
            type:'post',
            url:'php/login.php',
            param:{captcha:oForm.captcha.value}
        }).then(function (returnValue) {
            console.log(returnValue);
            if (returnValue == 1) {
                return ajax({
                    type:'post',
                    url:'php/login.php',
                    param:{account:oForm.account.value}
                });
            }
            else{
                console.log("yanzhengma")
                oRemindInfo.style.color="#ff3333";
                oRemindInfo.innerHTML="!验证码输入有误!";
                return ;
            }
        }).then(function (returnValue) {
            console.log(returnValue);
            if (typeof(returnValue)!=="undefined") {
                if (returnValue == 1) {    
                    return ajax({
                        type:'post',
                        url:'php/login.php',
                        param:{password:oForm.password.value}
                    });
                }
                else{
                    oRemindInfo.style.color="#ff3333";
                    console.log(oRemindInfo.style.color)
                    oRemindInfo.innerHTML="!账号输入有误!";
                }
            }
        }).then(function (returnValue) {
            console.log(returnValue);
            if (typeof(returnValue)!=="undefined") {
                if (returnValue == 1) {    
                    oRemindInfo.style.color="#000000";
                    oRemindInfo.innerHTML="√ 登录成功!";
                }
                else{
                    oRemindInfo.style.color="#ff3333";
                    oRemindInfo.innerHTML="!密码输入有误!";
                }    
            }    
        }).catch(function (err) {
            console.log("错误代码:" + err)
        });
    }
    login.js

     3.php代码文件(验证的数据都是后台写死的)

    <?php
    $captcha = isset($_POST['captcha']) ? $_POST['captcha'] : null;
    $account = isset($_POST['account']) ? $_POST['account'] : null;
    $password = isset($_POST['password']) ? $_POST['password'] : null;
    
    // 验证验证码
    if(!is_null($captcha)) {
        if($captcha == '11') {
            echo 1;
        } else {
            echo 0;
        }
    }
    
    // 验证账号
    if(!is_null($account)) {
        if($account == '11') {
            echo 1;
        } else {
            echo 0;
        }
    }
    
    // 验证密码
    if(!is_null($password)) {
        if($password == '11') {
            echo 1;
        } else {
            echo 0;
        }
    }
    login.php

    十、ajax的同源策略

    同源策略(same origin policy)是一种约定,是由netscape公司引入浏览器的,他是浏览器最核心最基本的安全功能。现在所有支持JS的浏览器都会使用这个策略。

    所谓同源:是指协议、域名、端口都相同。

    目的:保证用户信息的安全,防止恶意的网站窃取数据。

    限制范围:

    1:cookie,localStorage和IndexDB无法读取。

    2:DOM无法获得。

    3:AJAX请求不能发送。


    十一、jsonp跨域

    JSONP = JSON WITH PADDING。一种数据调用的方式。

    AJAX无论请求什么页面,因为同源策略的限制,只要是跨域就一律不准。不过我们发现凡是拥有src这个属性的标签都拥有跨域的能力,如img,iframe,script。那我们就可以把数据装载到JS文件内,供客户端调用和进一步处理。

    提供下百度和淘宝的跨域的方式(京东的可看不可用)

    淘宝:

    百度:

    步骤:

    1:定义一个回调函数。

    2:创建script标签,指定src地址,并添加到页面中。

    注:src必须跟着一个参数callback来指定回调函数名称。(callback回调函数定义必须全局不能放到匿名函数或函数表达式,否则无法找到)

    利用127.0.0.1和localhost本地环境演示jsonp跨域demo

    (依据同源策略的概念可以看出127.0.0.1和localhost属于跨域,可通过实际请求测试若不进行跨域访问无法访问到127.0.0.1资源)

    1.html页面 

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>jsonp跨域请求</title>
    </head>
    <body>    
    </body>
    <script type="text/javascript" src="http://127.0.0.1/project/jsonp/jsonp.js"></script>
    /*凡是拥有src这个属性的标签都拥有跨域的能力,如img,iframe,script。那我们就可以把数据装载到JS文件内,供客户端调用和进一步处理。*/
    </html>
    jsonp.html

     2.jsonp.js文件 

    /*回调函数(全局,否则无法找到)接收并处理跨域请求的返回的数据*/
    function callback(data) {
        var data="跨域请求的数据结果:"+data
        console.log(data)
        document.body.innerHTML=data;
    }
    window.onload=function () {
        var oScript=document.createElement('script');
        /*创建script标签*/
        oScript.src="http://127.0.0.1/project/jsonp/jsonp.php?callback=callback";
        /*设置script标签src属性值为跨域请求的文件并指定接收返回结果的回调函数*/
        document.body.appendChild(oScript);
        /*创建的标签加载到body页面*/
        document.body.removeChild(oScript)
        /*跨域请求完成标签废弃,进行移除*/
    }
    jsonp.js

     3.php文件 

    <?php
        $arr = array(
            '测试数据1!',
            '测试数据2!',
            '测试数据3!'
        );
        $arr = json_encode($arr);
        //数组格式转换为字符串
        $callback = $_GET['callback'];
        //获取回调函数的名称
        echo $callback.'('.$arr.')';
        //返回回调函数并传参
    ?>
    jsonp.php 

    十二、服务器端跨域

    CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

    浏览器发起CORS请求,都会在请求头信息中增加一个Origin字段。该字段用来说明本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,来决定是否同意这次请求。

    实现方式:

    在服务器端指定Access-Control-Allow-Origin头信息,该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。如果Origin指定的值不在许可范围内,服务器返回一个正常的HTTP响应。浏览器如果发现响应头信息中没有包含Access-Control-Allow-Origin字段就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。

    向服务器发送Cookie: 

    1.在服务器端指定Access-Control-Allow-Credentials头信息,该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。 

    2.开发者必须在AJAX中打开withCredentials字段。 

    3.要发送cookie,Access-Control-Allow-Origin的值不能是*,必须指定明确的,与请求网页一致的域名。同时cookie依然遵循同源策略,请求源与被请求源必须拥有同一个主域名,否则cookie还是不会被发送。

          case 1:www.a.com访问bbs.a.com,并且cookie的domain设置为.a.com,那么cookie才会随着CORS发送给后台。 

         case 2:www.a.com访问www.b.com,这种情况下,cookie是不会随着CORS发送给后台。

     (备注本想这里coding demo来说明,然没主域名这样的条件,后续有机会补上......)


    十三、get和post提交

    1:功能

    get多用于从服务器上获取数据,post多用于向服务器发送数据。

    2:数据提交

    GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连。POST把提交的数据则放置在是HTTP包的包体中。

    3:数据大小

    GET请求传送的数据量比较小,达能大于2KB,这个受浏览器的限制。而POST请求传送的数据量比较大,一般默认为不受限制。其实大小受服务器的限制。

    4:安全性

    GET安全性非常低,POST的安全性较高。

  • 相关阅读:
    linux初学者-ftp篇(一)
    随机密码生成
    倒计时问题java
    百钱买鸡
    去7JAVA
    贪吃蛇Controller Java实现(二)
    贪吃蛇Global Java实现(二)
    贪吃蛇GamePanel Java实现(二)
    贪吃蛇Listener Java实现(二)
    贪吃蛇snake Java实现(二)
  • 原文地址:https://www.cnblogs.com/witkeydu/p/8512057.html
Copyright © 2011-2022 走看看