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的安全性较高。

  • 相关阅读:
    wget(转)
    852. Peak Index in a Mountain Array
    617. Merge Two Binary Trees
    814. Binary Tree Pruning
    657. Judge Route Circle
    861. Score After Flipping Matrix
    832. Flipping an Image
    461. Hamming Distance
    654. Maximum Binary Tree
    804. Unique Morse Code Words
  • 原文地址:https://www.cnblogs.com/witkeydu/p/8512057.html
Copyright © 2011-2022 走看看