天天看點

Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise

伺服器相關概念

1. 伺服器

簡而言之就是在網上提供服務的計算機,它的本質就是網絡上的一台配置非常高的電腦。 我們是看不到的,但我們經常使用它為我們提供的服務,比如: ​ 微信 ​ 網易雲音樂 ​ 浏覽器

2. 用戶端(浏覽器端)

簡單了解就是我們的個人電腦。個人電腦去通路伺服器提供的服務。

3. 通訊過程

以咱們用的最多的浏覽器為例,和伺服器通訊的過程就像聊微信?
  1. 我:---> 你好?在嗎?欠我的錢,什麼時候還?
  2. 他:--->不在!不還!
Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise
:point_right: 一次通訊有兩部分組成:請求 與 響應

5. url位址格式及作用

  1. url 格式
    • 協定名
      • http:
      • https:
      • ftp:
    • 主機名
      • ip位址
        網絡中電腦的唯一辨別
      • 域名:為了友善記憶,例如: www.baidu.com
        最終也要轉換成ip位址
    • 端口号
      計算機安裝的軟體在進行網絡通訊時的辨別。
      Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise
    • 請求資訊
      http://www.thinker.com:8080/請求資訊 端口号後面的即為請求資訊,具有如下含意:
      • 請求伺服器上的什麼檔案,
      • 本次請求給向伺服器攜帶了什麼樣的資料,
      • 本次請求有什麼目的
      • 等等......
  • AJAX 概念

    認識了伺服器之後,咱們來認識以下

    ajax

    ,并且體驗以下他能夠實作的效果

    概念

    AJAX 是異步的 JavaScript 和 XML(Asynchronous JavaScript And XML)。簡單點說,就是使用

    XMLHttpRequest

    對象與伺服器通信。 它可以使用 JSON,XML,HTML 和 text 文本等格式發送和接收資料。AJAX 最吸引人的就是它的“異步”特性,也就是說它可以在不重新重新整理頁面的情況下與伺服器通信,交換資料,或更新頁面。

    AJAX 應用最主要的兩個特點:

    • 在不重新加載頁面的情況下發送請求給伺服器。
    • 接受并使用從伺服器發來的資料。
    • 不但要向伺服器發起請求,還要能夠接收伺服器響應的結果。這一點初學者一定要意識到。

請求封包&響應封包

http - 請求封包

浏覽器 與 伺服器進行通訊時,每次浏覽器發出的請求,叫請求封包

組成 與 檢視方法

請求封包組成:
  1. 請求行
  2. 請求頭
  3. 空行
  4. 請求體

具體格式如下圖:

Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise

小結

  • 請求封包是自動生成,還是人為設定的?
    1. 由浏覽器自動生成,也可以人為的修改或添加
  • 請求的方法和位址
    • 在請求封包的哪裡?
    • 請求行中

http - 響應封包

組成 與 檢視方法

浏覽器 與 伺服器進行通訊時,每次伺服器的響應,叫響應封包

響應封包由:
  1. 狀态行
  2. 響應頭部
  3. 空行
  4. 響應體

具體格式如下圖:

Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise

小結

  • 響應封包中狀态碼在哪裡?

    狀态行

  • 響應封包中的伺服器傳回的内容在哪裡?

    響應(主)體

http - 響應狀态碼

伺服器響應的内容中除了響應體以外,還有一個需要重點關注的資訊,

http狀态碼

伺服器對本次請求所處理的結果以一個編碼進行展現,這個狀态碼是我們需要關注的。

傳送門:MDN-HTTP狀态碼 HTTP response status codes

概念 與 作用

  • 概念
狀态碼反應了,伺服器對本次請求所處理的結果,由三位數字組成。
  • 作用
狀态碼會作為前端人員判斷請求處理結果的依據。
Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise

常見的狀态碼

不僅僅隻有這幾個,這裡隻是列舉了常見的

狀态碼 狀态碼描述 說明
200 OK 請求成功。
201 Created 資源在伺服器端已成功建立。
304 Not Modified 資源在用戶端被緩存,響應體中不包含任何資源内容!
400 Bad Request 用戶端的請求方式、或請求參數有誤導緻的請求失敗!
401 Unauthorized 用戶端的使用者身份認證未通過,導緻的此次請求失敗!
404 Not Found 用戶端請求的資源位址錯誤,導緻伺服器無法找到資源!
500 Internal Server Error 伺服器内部錯誤,導緻的本次請求失敗!

小結

狀态碼很多,但對于前端更多的是關注以下幾個狀态碼:

  • 200
    請求被成功處理
  • 401
    在涉及到身份認證操作時,身份認證失敗
  • 404
    url位址錯誤
  • 400
    請求參數錯誤

    XMLhttpRequest介紹

    Ajax(Asynchronous JavaScript and XML)不是指一種單一的技術,而是有機地利用了一系列相關的技術。雖然其名稱包含XML,但實際上資料格式可以由JSON代替,進一步減少資料量,形成所謂的AJAJ。為了使用JavaScript向伺服器發出 HTTP 請求,需要一個提供此功能的類的執行個體。這就是XMLHttpRequest的由來。這樣的類最初是在Internet Explorer中作為一個名為XMLHTTP的ActiveX對象引入的。然後,Mozilla,Safari和其他浏覽器,實作一個XMLHttpRequest類,支援Microsoft的原始ActiveX對象的方法和屬性。同時微軟也實作了XMLHttpRequest
    • header

      将要被指派的請求頭名稱
    • value

      給指定的請求頭賦的值
  • 顯而易見XMLHttpRequest類是重中之重了。

    XMLhttpRequest屬性

  • onreadystatechange

    一個JavaScript函數對象,當readyState屬性改變時會調用它。回調函數會在user interface線程中調用。

    readyState

    HTTP 請求的狀态.當一個 XMLHttpRequest 初次建立時,這個屬性的值從 0 開始,直到接收到完整的 HTTP 響應,這個值增加到 4。

    5 個狀态中每一個都有一個相關聯的非正式的名稱,下表列出了狀态、名稱和含義:

    狀态 名稱 描述
    Uninitialized 初始化狀态。XMLHttpRequest 對象已建立或已被 abort() 方法重置。
    1 Open open() 方法已調用,但是 send() 方法未調用。請求還沒有被發送。
    2 Sent Send() 方法已調用,HTTP 請求已發送到 Web 伺服器。未接收到響應。
    3 Receiving 所有響應頭部都已經接收到。響應體開始接收但未完成。
    4 Loaded HTTP 響應已經完全接收。

    readyState 的值不會遞減,除非當一個請求在處理過程中的時候調用了 abort() 或 open() 方法。每次這個屬性的值增加的時候,都會觸發 onreadystatechange 事件句柄。

    responseText

    目前為止為伺服器接收到的響應體(不包括頭部),或者如果還沒有接收到資料的話,就是空字元串。

    如果 readyState 小于 3,這個屬性就是一個空字元串。當 readyState 為 3,這個屬性傳回目前已經接收的響應部分。如果 readyState 為 4,這個屬性儲存了完整的響應體。

    如果響應包含了為響應體指定字元編碼的頭部,就使用該編碼。否則,假定使用 Unicode UTF-8。

    responseXML

    對請求的響應,解析為 XML 并作為 Document 對象傳回。

    status

    由伺服器傳回的 HTTP 狀态代碼,如 200 表示成功,而 404 表示 "Not Found" 錯誤。當 readyState 小于 3 的時候讀取這一屬性會導緻一個異常。

    statusText

    這個屬性用名稱而不是數字指定了請求的 HTTP 的狀态代碼。也就是說,當狀态為 200 的時候它是 "OK",當狀态為 404 的時候它是 "Not Found"。和 status 屬性一樣,當 readyState 小于 3 的時候讀取這一屬性會導緻一個異常。

    XMLHttpRequest方法

    abort()

    取消目前響應,關閉連接配接并且結束任何未決的網絡活動。

    這個方法把 XMLHttpRequest 對象重置為 readyState 為 0 的狀态,并且取消所有未決的網絡活動。例如,如果請求用了太長時間,而且響應不再必要的時候,可以調用這個方法。

    getAllResponseHeaders()

    把 HTTP 響應頭部作為未解析的字元串傳回。

    如果 readyState 小于 3,這個方法傳回 null。否則,它傳回伺服器發送的所有 HTTP 響應的頭部。頭部作為單個的字元串傳回,一行一個頭部。每行用換行符 "\r\n" 隔開。

    getResponseHeader()

    傳回指定的 HTTP 響應頭部的值。其參數是要傳回的 HTTP 響應頭部的名稱。可以使用任何大小寫來制定這個頭部名字,和響應頭部的比較是不區分大小寫的。

    該方法的傳回值是指定的 HTTP 響應頭部的值,如果沒有接收到這個頭部或者 readyState 小于 3 則為空字元串。如果接收到多個有指定名稱的頭部,這個頭部的值被連接配接起來并傳回,使用逗号和空格分隔開各個頭部的值。

    open()

    初始化一個請求. 該方法用于JavaScript代碼中;如果是本地代碼, 使用 openRequest()方法代替.

    注意: 在一個已經激活的request下(已經調用open()或者openRequest()方法的request)再次調用這個方法相當于調用了abort()方法。
    參數
    • method

      請求所使用的HTTP方法; 例如 "GET", "POST", "PUT", "DELETE"等. 如果下個參數是非HTTP(S)的URL,則忽略該參數.
    • url

      該請求所要通路的URL
    • async

      一個可選的布爾值參數,預設為true,意味着是否執行異步操作,如果值為false,則send()方法不會傳回任何東西,直到接受到了伺服器的傳回資料。如果為值為true,一個對開發者透明的通知會發送到相關的事件監聽者。這個值必須是true,如果multipart 屬性是true,否則将會出現一個意外。
    • user

    • 使用者名,可選參數,為授權使用;預設參數為空string.
    • password

      密碼,可選參數,為授權使用;預設參數為空string.

    send()

    發送 HTTP 請求,使用傳遞給 open() 方法的參數,以及傳遞給該方法的可選請求體。

    setRequestHeader()

    向一個打開但未發送的請求設定或添加一個 HTTP 請求(設定請求頭)。

    參數

Ajax原生實作

  1. 執行個體化

    XMLHttpRequest

    異步對象
    let xhr = new XMLHttpRequest() XMLHttpRequest 是内置的異步對象
  2. 設定 請求方式 與 請求位址
    xhr.open( 請求方式, 請求位址 ) 相當于axios配置對象裡的 url與method
  3. 發送請求
  4. xhr.send()
  5. 注冊 處理響應的回調函數
    xhr.onload = function(){} 相當于then()裡的回調函數

原生git傳參

  • get方法如何傳遞參數
  • 直接在url後拼接即可, url?key=value&key=value
<script>
        document.querySelector('button').onclick = function () {
            // 1. 執行個體化 `XMLHttpRequest` 異步對象
            let xhr = new XMLHttpRequest()
​
            // 2. 設定 請求方式 與 請求位址
            xhr.open('get', 'https://autumnfish.cn/api/joke?name=zs&age=20')
​
            // 3. 發送請求
            xhr.send()
​
            // 4. 注冊 處理響應的回調函數
            xhr.onload = function () {
                console.log(xhr.response);
            }
        }
    </script>      

原生post傳資料

文法

  • xhr.send(資料)
    • 用于向伺服器發送資料
  • xhr.setRequestHeader('content-type', '資料的格式')

    隻要是資料就要明确的告訴伺服器所發送的資料的格式是什麼! axios會自動根據資料格式,自動設定content-type, 由于是原生文法,是以要手動設定content-type 👉 setRequestHeader()要在send()之前設定,因為隻要執行了send()本次請求就完成了,後面的代碼與本次的請求沒有任何關系了。

<script>
     document.querySelector('button').onclick = function(){
     let xhr = new XMLHttpRequest()
            
     xhr.open('post', 'https://autumnfish.cn/api/form/urlencoded')
            
     xhr.onload = function(){
     console.log(xhr.response);
     }
    
     // 設定content-type
     xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
     // urlencoded格式
     let data = 'username=thinker&age=20'
     xhr.send(data)
      }
 </script>      

傳遞 json 格式

  • 請求位址:https://autumnfish.cn/api/form/json
  • 請求方法:

    post

  • 請求參數:
    • 說明:

      content-type

      application/json

    • 送出的資料格式為

      JSON

  • 測試:
    • 根據接口文檔要求通過

      setRequestHeader

      設定

      content-type

      請求頭
    • 通過

      send

      方法送出符合格式要求的資料,并确認結果(可以通過JSON.stringify轉化格式)
<script>
    document.querySelector('button').onclick = function(){
    let xhr = new XMLHttpRequest()
            
    xhr.open('post', 'https://autumnfish.cn/api/form/json')
            
    xhr.onload = function(){
  // xhr.response接收響應的資料
           console.log(xhr.response);
​
  // JSON.parse解析接收到的響應資料
            console.log(JSON.parse(xhr.response));
        }
            
 // 設定content-type
    xhr.setRequestHeader('content-type', 'application/json')
 // JSON
   let data = {username:'thinker',age:20};
   let strJSON = JSON.stringify(data)
            
   xhr.send(strJSON)
     }      

接收 與 解析

  • 接收
    • 原生ajax請求後,伺服器響應的資料要通過 xhr.response來接收
  • 解析

    伺服器傳回的資料也有多種格式之分,現在使用最廣泛的就是json,早期還用過xml。 隻要是接口傳回的資料幾乎都是JSON格式。 解析響應資料也就是解析JSON格式的資料,使用JSON.parse(),如果是向伺服器傳遞則使用JSON.stringify()

Axios

  • AJAX是一種技術,在不重新加載頁面的情況下發送請求給伺服器。但是原生的代碼晦澀難懂,對于初學者很不友好。
  • 有一些人對原生代碼進行了封裝,簡化了原生代碼的操作。初學者就可以使用簡化後的代碼完成ajax操作。
  • axios

    是目前最為流行的代碼庫,在浏覽器端是基于

    Ajax

    封裝

axios - get請求文法

文法一

// 無參數
axios.get(url)
​
// 有參數,參數拼接在URL中
axios.get(url?key=value&key=value)
--------------------------------------
// 1. 無參請求
      axios.get('https://autumnfish.cn/api/joke').then(function(response){
        console.log(response);
      })
   
// 2. 有參數,通過url傳遞
      axios.get('https://autumnfish.cn/api/joke/list?num=3').then(function(response){
     console.log(response);      

文法二(推薦)

使用 params 發起帶參請求

推薦方式
axios({
    method:'GET',
    url:'https://autumnfish.cn/api/joke',
    params:{num:3}
    }).then(function(res){console.log(res)})      

axios - post請求

文法一

axios.post(url,{key:value,key:value}).then(function(response){
console.log(response)
})      

文法二(推薦)

data

是作為請求體被發送的資料

  • 僅适用 PUT , POST , DELETE 和 ATCH 請求方法
推薦方式
axios({
    method:'POST',
    url:'https://autumnfish.cn/api/user/check',
    data:{name:zs,age:18}
    }).then(function(res){console.log(res)})      

設定content-type 文法

  • 如果是人為的使用axios向伺服器發送資料,就需要設定相應的 content-type
  • 前端,隻會向伺服器發送資料,是以隻需要設定請求封包的 content-type即可

具體參考MDN

axios 設定 content-type

axios 在配置對象中通過 headers 來設定content-type,格式:
axios.post(url,data,{headers:{'content-type':'内容格式類型'}})
​
axios({
    url:'',
    method:'post',
    data:{},
    headers:{
        'content-type':'内容格式類型'
    }
})      

接口中的 content-type

不同接口對于送出資料格式的要求略有不同,咱們結合3個測試用接口,來看看如何通過

axios

如何送出不同格式的資料,之後看到類似的需求能夠選擇對應的格式進行送出

測試接口

1.FormData資料送出 接口

  • 請求位址:https://autumnfish.cn/api/form/formdata
  • 請求方法:

    post

  • 請求參數:
    • 說明:

      content-type

      multipart/form-data

    • 送出

      FormData

      即可
axios({
          url:'https://autumnfish.cn/api/form/formdata',
          method:'post',
          data:fd,
          headers:{'content-type':'multipart/form-data'}
  })      

2.application/json資料送出 接口

  • 請求位址:https://autumnfish.cn/api/form/json
  • 請求方法:

    post

  • 請求參數:
    • 說明:

      content-type

      application/json

    • 送出JS對象即可
axios({
          url:'https://autumnfish.cn/api/form/json',
          method:'post',
          data:{
            name:'thinker',
            age:20,
          },
          headers:{'content-type':'application/json'}
 })      

3.application/x-www-form-urlencoded資料送出 接口

  • 請求位址:https://autumnfish.cn/api/form/urlencoded
  • 請求方法:

    post

  • 請求參數:
    • 說明:

      content-type

      application/x-www-form-urlencoded

    • 通過data送出

      key=value&key2=valu2

      這種格式
axios({
      url:'https://autumnfish.cn/api/form/urlencoded',
     method:'post',
     data:{
        name:'thinker',
        age:20,
        },
     headers:{'content-type':'application/x-www-form-urlencoded'}
   })      

axios預設配置

全局預設請求基位址
axios.defaults.baseURL = 'https://api.example.com';
​
全局預設token
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
​
全局預設請求頭配置
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';      

攔截器

在請求或響應被 then 或 catch 處理前攔截它們。

// 添加請求攔截器
axios.interceptors.request.use(function (config) {
    // 在發送請求之前做些什麼
    return config;
  }, function (error) {
    // 對請求錯誤做些什麼
    return Promise.reject(error);
  });
​
// 添加響應攔截器
axios.interceptors.response.use(function (response) {
    // 2xx 範圍内的狀态碼都會觸發該函數。
    // 對響應資料做點什麼
    return response;
  }, function (error) {
    // 超出 2xx 範圍的狀态碼都會觸發該函數。
    // 對響應錯誤做點什麼
    return Promise.reject(error);
  });      

示範

axios.interceptors.request.use(function(config){
            console.log('請求攔截器執行了')
            return config
        },function(err){})
​
        axios.interceptors.response.use(function(res){
            console.log('響應攔截器執行了')
            const a = 'jh'  //設定響應的資料
            return a
        },function(err){})
        axios({
            method:'POST',
            url:'http://ajax-api.itheima.net/register',
            data:{
                username:'ahsdfsdddjkmzzh',
                password:'123456'
            },
            a:(function() {
                console.log('請求')})()
            }
        ).then(function(res){
            console.log('響應')
            console.log(res)
        })
-------------------------------------------
//執行順序
                    請求
設定攔截器.html:14   請求攔截器執行了
設定攔截器.html:19   響應攔截器執行了
設定攔截器.html:34   響應
設定攔截器.html:35   jh      

如果你稍後需要移除攔截器,可以這樣:

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);      

可以給自定義的 axios 執行個體添加攔截器。

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});      

錯誤處理

請滾去看文檔

axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // 請求成功發出且伺服器也響應了狀态碼,但狀态代碼超出了 2xx 的範圍
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // 請求已經成功發起,但沒有收到響應
      // `error.request` 在浏覽器中是 XMLHttpRequest 的執行個體,
      // 而在node.js中是 http.ClientRequest 的執行個體
      console.log(error.request);
    } else {
      // 發送請求時出了點問題
      console.log('Error', error.message);
    }
    console.log(error.config);
  });      

form表單送出資料

  • 真實項目中的資料都是由浏覽者輸入的,而form表單就是被設計用來收集浏覽者輸入的資料,form表單不但具有采集資料的作用,還有送出資料的能力(不需要JavaScript)。
  • 但原生的送出方式會造成頁面跳轉,是以這種方式現在幾乎不再使用,了解即可。

form原生送出

  1. form - 原生送出資料的基本配置
    • form标簽的action屬性用于設定伺服器接
      用于設定伺服器接口 form标簽内所有的表單元素都會送出到action指向的url
    • form标簽的method屬性
      用于設定請求的方式 get或post
    • 所有的表單元素必須設定name屬性
      name屬性用于設定伺服器所接收的資料項的key
    • 送出按鈕 submit

示例

<body>
     action用于設定表單送出時所請求的接口
     method用于設定請求方式
<form action="請求url 位址" method="請求方式">
       name 屬性值 将作為 key
       表單輸入的内容 将作為 value
   <input class="username" type="text" placeholder="使用者名" name="username">
   <input class="password" type="password" placeholder="密碼" name="password">
   <input class="submit" type="submit" value="送出">
</form>
</body>      

ajax送出資料

雖然form表單的原生送出資料已成為過去,但我們仍然需要使用form表單來收集資料,而使用ajax送出,來替換原生的送出

步驟

  1. 阻止預設行為
    由于ajax送出,與form表單送出都需要通過點選 送出按鈕。而form表單的原生送出屬于表單的預設行為,是以需要阻止這個預設行為,而執行js的ajax送出行為
  2. 收集表單資料
    擷取表單資料,組織成 axios 的data 參數
  3. 發起ajax請求
    按之前的方式發起ajax請求, 選擇方法,設定url,稍許的不同在于資料是從表單中擷取到的。

示例

<script>
        let oBtn = document.querySelector('.submit')
       // 為按鈕 .submit 按鈕注冊事件
        let oBtn = document.querySelector('.submit')
        oBtn.onclick = function(e){
            // 阻止表單的預設行為
            e.preventDefault()
            
            // 收集表單資料
            let data = {
                username:document.querySelector('.username').value,
                password:document.querySelector('.password').value
            }
​
            // console.log(data);
            // 發起 ajax 請求
            axios.get("https://autumnfish.cn/api/form/submit",{params:data}).then(function(res){
                console.log(res);
            })
​
    </script>      

但是:這種方法需要自己 自己構造key:value資料很繁瑣

form-serialize插件

插件下載下傳位址

如果form表單内有很多的表單項,取值的代碼也會有很多,這一節咱們來學習form-serialize插件來簡化取值

使用步驟

  • 1.引入form-serialize
    引入後會在全局注冊一個serialize()方法
  • 2.調用serialize即可得到表單内所有的資料

    serialize(form标簽對象, {hash:true})

    表單資料被組織成對象 {key:value, key:value}
    serialize(form标簽對象, {hash:false})
    表單資料被組織成 key=value&key=value 格式的鍵值對
    注意:表單元素必須有name屬性,

示例

<!-- 1. 導入 form-serialize插件 -->
 <script src="./02-其他資料/lib/form-serialize.js"></script>
    <script>
        let oBtn = document.querySelector('.submit')
        oBtn.onclick = function(e){
            e.preventDefault()
​
            // 2. 調用 form-serialize方法
            let oForm = document.querySelector('form')
            let data = serialize(oForm, { hash: true })
            // console.log(data);
​
        axios.get("https://autumnfish.cn/api/form/submit",                          {params:data}).then(function(res){
                console.log(res);
            })
            
        }
 </script>      

FormData 基本用法

上一節咱們是通過插件來擷取表單資料,JavaScript提供了一個内置對象

FormData

,也可以實作類似效果,而且不僅僅是文本類資料,檔案也可以,咱們先嘗試文本類的資料

  • 這裡隻介紹了常用的,詳細見文檔:MDN-FormData

簡介

  • FormData是浏覽器内置的對象,其作用等價于 form-serialize。
  • FormData對象内部使用key value形式存儲表單資料項
  • 能夠結合ajax進行操作

基本文法

  • 1.執行個體化FormData對象
    • new FormData(form标簽對象)
    由form标簽建立FormData對象,在有form标簽的情況下
    • new FormData()
    建立一個空的FormData對象,在沒有form标簽的情況下使用
  • 2.執行個體上的方法
    • .get(key)

    傳回在 FormData

    對象中與給定鍵關聯的第一個值
    • .append(key, value)

    FormData

    中添加新的屬性值,

    FormData

    對應的屬性值存在也不會覆寫原值,而是新增一個值,如果屬性不存在則新增一項屬性值
    • .set(key,value)

    FormData

    設定屬性值,如果

    FormData

    對應的屬性值存在則覆寫原值,否則新增一項屬性值

示例

<script>
        let oBtn = document.querySelector('.submit')
        oBtn.onclick = function(e){
            e.preventDefault()
​
            // 1. 基于表單 執行個體化 FormData 對象
            let oForm = document.querySelector('form')
            
            let fd = new FormData(oForm)
            // console.log(fd);
            // 2. 直接将 FormData 執行個體作為資料傳遞
         axios.get("https://autumnfish.cn/api/form/submit",                          {params:fd}).then(function(res){
                console.log(res);
        })    
    }
</script>      

檔案上傳表單 - 補充

accept屬性

accept屬性用于過濾出指定類型的檔案供選擇 MDN傳送門

   <input accept="image/png, image/jpg" type="file" name="avatar" placeholder="請選擇頭像">
    <input accept=".png, .jpg" type="file" name="avatar" placeholder="請選擇頭像">      

onchange事件

選擇的檔案變更時觸發此事件

<script>
    // 選擇的檔案變更時觸發此事件
    document.querySelector('input').onchange = function(){
        // console.log('hello');            
    }
</script>      

擷取檔案對象

<script>
    // 選擇的檔案變更時觸發此事件
    document.querySelector('input').onchange = function(e){
        // console.log('hello');
        // console.log(this.value);         // 擷取的是選擇的路徑名,這一個字元串
        console.log(e.target.files[0]);     // 擷取選擇的檔案對象
    }
​
</script>      

小結

  • accept屬性是否可限制使用者選擇檔案
    不可以,僅是簡單的過濾
  • onchange事件什麼時候觸發
    當選擇的檔案有所變化時
  • 如何擷取 檔案對象
    e.target.files[0]

    promise

promise-介紹

Promise

,譯為承諾,是異步程式設計的一種解決方案,比傳統的解決方案(回調函數)更加合理和更加強大

  • 在以往我們如果處理多層異步操作,我們往往會像下面那樣編寫我們的代碼
setTimeout(function(){
            console.log('第1步');
            setTimeout(function(){
                console.log('第2步');
                setTimeout(function(){
                    console.log('第3步');
                    setTimeout(function(){
                        console.log('第4步');
                    },1000)
                },3000)
            },2000)
        },4000)      
  • 閱讀上面代碼,是不是很難受,上述形成了經典的回調地獄
  • 現在通過

    Promise

    的改寫上面的代碼
new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve('第 1 步')
            }, 3000)
        }).then(function (res) {      // 屬于第1個new Promise執行個體
            console.log(res);  //傳回上一個執行成功的資料
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                    resolve('第 2 步')
                }, 2000)
            })
        }).then(function (res) {
            console.log(res);
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                    resolve('第 3 步')
                }, 5000)
            })
        }).then(function (res) {
            console.log(res)
        })      

瞬間感受到

promise

解決異步操作的優點:

  • 鍊式操作減低了編碼難度
  • 代碼可讀性明顯增強

下面我們正式來認識 promise:

Promise - 基本文法

  • 傳送門:MDN-Promise基本示例
  • 傳送門:MDN-Promise.prototype.then
  • 傳送門:MDN-Promise.prototype.catch

概念

Promise是一個對象,内部執行指定異步任務,并在成功後失敗時執行預定的處理代碼。

基本文法

Promise相關的有三要素:所有執行異步任務,成功後的代碼,失敗後的代碼,這三要素都要展現在文法裡。

文法格式

  1. 1.Promise是一個對象

    new Promise()

  2. 2.Promise執行個體化時必須傳遞一個回調函數

    new Promise( function(){} )

  3. 3.回調函數必須有兩從此參數

    new Promise( function(resolve, reject){ })

回調函數的使用原則

  1. 1.回調函數内部主要用于執行異步操作(時間不确定的操作)

    new Promise( function( resolve, reject ){ // 這裡執行異步操作 })

  2. 2.根據異步操作結果的選擇性的調用resolve 或 reject

    new Promise( function(resolve, reject){

    let 結果 = 這裡執行異步操作

    if(結果成功){

    resolve(結果)

    }else{

    reject(失敗) }

    })

這裡一定要注意,異步操作雖然在回調函數内部執行,但回調函數的結果并不在回調函數内處理。

分情況處理結果

new Promise( function(resolve, reject){

let 結果 = 這裡執行異步操作

if(成功){

resolve(結果)

}else{

reject(失敗)

}

}).then( function(data){

// resolve會跳到then裡執行,data就是resolve()傳遞過來的資料

}).catch( function(err){

// reject會跳到catch裡執行,err就是reject()傳遞過來的錯誤

})

說明:

  • new Promise()
    建立對象,執行異步任務,根據結果選擇性執行resolve(資料) 或 reject(錯誤) resolve()與reject()并不是處理結果,隻是通過這種文法将成功情況的代碼,與失敗情況的代碼放到Promise回調函數的外面進行處理,由此減少了一層函數的嵌套。
  • then() 成功時的處理
    Promise回調函數,執行異步任務時,如果異步結果是失敗的,将會使用reject,将錯誤 導向catch代碼塊 進行處理。
  • catch() 失敗時的處理
    Promise的catch方法,用于處理reject傳遞過來的錯誤,
  • finally() 方法用于指定不管 Promise 對象最後狀态如何,都會執行的操作
  • promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});

可以這樣了解,通過 Prmoise 來執行異步函數,可以在回調函數外部處理異步操作的結果

示例代碼

<script>
    let pro = new Promise(function(resolve,reject){
        // setTimeout代表一個異步操作
        setTimeout(function(){
            // 模拟異步操作産生的資料
            let v = Math.floor(Math.random()*10 + 1);
            // 将成功或失敗的發送到其他代碼塊
            if(v % 2 ==0){
                resolve('成功的資料')
            }else{
                reject('失敗的原因')
            }
        })
    })
​
    pro.then(function(data){
        // 成功時處理的代碼塊
        console.log('成功 then被執行');
    }).catch(function(err){
        // 失敗時處理的代碼塊
        console.log('失敗 catch被執行');
    })
</script>      

小結

  • 1.如何了解Promise?
    Promise本質就是一個對象,用于執行指定的異步任務的工具代碼。
  • 2.Promise文法兩部分?
    建立對象并指派任務 分情況處理結果
  • 3.建立Promise對象時相關的參數要求?
    提供回調函數,回調函數裡定義兩個參數resolve與reject
  • 4.resolve與reject分别什麼時候調用?
    resolve成功時調用,傳遞資料 reject失敗時調用,傳遞錯誤資訊
  • 5.resolve、reject與then、catch的關系?
    resolve對應then reject對應catch

Promise - 鍊式調用

  • 文法
    new Promise().then().then().then()...
<script>
        new Promise(function(resolve,reject){
            resolve('hello 1')
        }).then(function(res){      // 屬于第1個new Promise執行個體
            console.log(res);
            return new Promise(function(resolve,reject){
                resolve('hello 2')
            })
        }).then(function(res){
            console.log(res);
            return new Promise(function(resolve,reject){
                resolve('hello 3')
         })
        }).then(function(res){
            console.log(res);
        })
    </script>      

小結

  • then()屬于哪個Promise對象
    • 前面的最近的then裡的return 的Promise執行個體對象

Promise的三種狀态

在Promise執行的過程中,内部會經曆三種狀态:

pendding

fulfilled

rejected

.傳送門:MDN-Promise

Ajax,axios,promise,formData學習筆記伺服器相關概念Axiospromise
  • 小結:三種狀态
    1. pendding :初始狀态,等待結果 對應的代碼為new Promise()
    1. fulfilled :收到結果,結果是成功 對應的代碼為resolve()
    1. rejected :收到結果,結果是失敗 對應的代碼為reject()

實際開發中,我們更多使用的是别人提取好的,例如: axios

promise- 構造函數方法

Promise

構造函數存在以下方法:

  • all()
  • race()
  • allSettled()
  • resolve()
  • reject()
  • try()

Promise.all

Promise除了按順序一次一個一次一個的執行異步任務外,還可以一次執行多個異步任務。 傳送門:Promise.all

  • all是靜态方法 靜态方法是通過函數 點 出來的方法
  • 文法:
Promise.all( [ promise1, promise2 , ... ] ).then(function(data){
​
}).catch(function(err){
​
})      

說明

  • 1.用數組存儲多個promise執行個體,每個執行個體指派了一個異步任務,再把這個數組傳遞給all方法,這樣就開始帶着多個任務執行
  • 2.

    Promise.all

    等待 所有都成功 或 任何一個失敗
    • 全部成功:then接收到的資料是所有的promise執行個體resolve的資料,是一個數組
    • 隻要有1個失敗:catch接收到的是第1個失敗的reject的錯誤.
    注意:最算有失敗的出現,每一個promise執行個體也要執行

示例代碼

<script>
    let proa = new Promise(function (resolve, reject) {
        resolve('proa resolve的結果');
        // reject('proba reject的錯誤')
    })
    let prob = new Promise(function (resolve, reject) {
        resolve('prob resolve的結果');
        // reject('prob reject的錯誤')
    })
    let proc = new Promise(function (resolve, reject) {
        resolve('proc resolve的結果');
        // reject('proc reject的錯誤')
    })
​
    // 傳遞多個 promise執行個體給 Promise.all()
    Promise.all([proa, prob, proc]).then(function (data) {
        console.log(data);
    }).catch(function (err) {
        console.log(err);
    })
</script>      

Promise.race

用于同時指派多個異步任務時使用,取第1個有結果的異步任務的結果作為Promise.race的結果。傳送門:Promise.race

文法:

Promise.race([ promise1, promise2, ... ]).then(function(data){
}).catch(function(err){
})      

說明

  • 用數組存儲多個promise執行個體,每個執行個體指派了一個異步任務,再把這個數組傳遞給race方法,這樣就開始帶着多個任務執行
  • Promise.race

    取第1個有結果的異步的結果作為Promise.race的結果。

    也就是第1個執行了resolve或reject的promise。注意不是傳遞的第1個,由于Promise裡的異步任務完成的時間是不同的,哪個Promise有結果,就取哪個結果作為Promise.race的結果

示例代碼

let proa = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('proa resolve的結果');
        // reject('proa reject的結果');
    }, 2000)
})
let prob = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('prob resolve的結果');
        // reject('prob reject的結果');
    }, 1000)
})
let proc = new Promise(function (resolve, reject) {
    setTimeout(function () {
        resolve('proc resolve的結果');
        // reject('proc reject的結果');
    }, 3000)
})
​
// 傳遞多個 promise執行個體給 Promise.race()
Promise.race([proa, prob, proc]).then(function (data) {
    console.log(data);
}).catch(function (err) {
    console.log(err);
})      
  • Promise.all 與 Promise.race的差別,及應用場景
    • Promise.all
      所有的都成功,或要有1個失敗 應用場景:頁面的渲染需要多個接口的資料
    • Promise.race
      等待第1個有結果的異步任務 應用場景:一個資料有多個接口可以擷取,等待最快的。

allSettled()

Promise.allSettled()

方法接受一組 Promise 執行個體作為參數,包裝成一個新的 Promise 執行個體

隻有等到所有這些參數執行個體都傳回結果,不管是

fulfilled

還是

rejected

,包裝執行個體才會結束

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
​
Promise.allSettled(promises).
  then((results) => results.forEach((result) => console.log(result.status)));
// 結果
// "fulfilled"
// "rejected"      

resolve()

将現有對象轉為

Promise

對象

Promise.resolve('foo')
// 等價于
new Promise(function(resolve){
    resolve('foo')
})      

參數可以分成四種情況,分别如下:

  • 參數是一個 Promise 執行個體,

    promise.resolve

    将不做任何修改、原封不動地傳回這個執行個體
  • 參數是一個

    thenable

    對象,

    promise.resolve

    會将這個對象轉為

    Promise

    對象,然後就立即執行

    thenable

    對象的

    then()

    方法
  • 參數不是具有

    then()

    方法的對象,或根本就不是對象,

    Promise.resolve()

    會傳回一個新的 Promise 對象,狀态為

    resolved

  • 沒有參數時,直接傳回一個

    resolved

    狀态的 Promise 對象

reject()

Promise.reject(reason)

方法也會傳回一個新的 Promise 執行個體,該執行個體的狀态為 rejected

const p = Promise.reject('出錯了');
// 等同于
const p = new Promise((resolve, reject) => reject('出錯了'))
​
p.then(null, function (err) {  // null代表沒有執行成功
  console.log(err)
});
// 出錯了      

Promise.reject()

方法的參數,會原封不動地變成後續方法的參數

Promise.reject('出錯了')
.catch(e => {
  console.log(e === '出錯了')
})
// true      

使用場景

将圖檔的加載寫成一個

Promise

,一旦加載完成,

Promise

的狀态就發生變化

const preloadImage = function (path) {
  return new Promise(function (resolve, reject) {
    const image = new Image();
    image.onload  = resolve;
    image.onerror = reject;
    image.src = path;
  });
};      

通過鍊式操作,将多個渲染資料分别給個

then

,讓其各司其職。或當下個異步請求依賴上個請求結果的時候,我們也能夠通過鍊式操作友好解決問題

// 各司其職
getInfo().then(res=>{
    let { bannerList } = res
    //渲染輪播圖
    console.log(bannerList)
    return res
}).then(res=>{
    
    let { storeList } = res
    //渲染店鋪清單
    console.log(storeList)
    return res
}).then(res=>{
    let { categoryList } = res
    console.log(categoryList)
    //渲染分類清單
    return res
})      

通過

all()

實作多個請求合并在一起,彙總所有請求結果,隻需設定一個

loading

即可

function initLoad(){
    // loading.show() //加載loading
    Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
        console.log(res)
        loading.hide() //關閉loading
    }).catch(err=>{
        console.log(err)
        loading.hide()//關閉loading
    })
}
//資料初始化    
initLoad()      

通過

race

可以設定圖檔請求逾時

//請求某個圖檔資源
function requestImg(){
    var p = new Promise(function(resolve, reject){
        var img = new Image();
        img.onload = function(){
           resolve(img);
        }
        //img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的
        img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";
    });
    return p;
}
​
//延時函數,用于給請求計時
function timeout(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            reject('圖檔請求逾時');
        }, 5000);
    });
    return p;
}
​
Promise
.race([requestImg(), timeout()])
.then(function(results){
    console.log(results);
})
.catch(function(reason){
    console.log(reason);
});      

如何保證一個promise失敗之後,promise.all還能正常收到結果

const promise1 = Promise.resolve(3);
const promise2 = Promise.reject(new Error());
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
​
Promise.all([promise1, promise2, promise3].map(p=>p.catch(e=>e))).then((values) => {
  console.log(values);
});      

async函數 與 await

  • Promise已經可以很好的解決回調嵌套問題啦,咋又來一個?他的作用是簡化Promise調用時的寫法,把最後一層then也給拿掉,使用 = 來接收異步的結果
  • 簡單粗暴的了解方式就是:不再使用 .then(function(資料){}),而直接使用 = 來接收資料 傳送門:async函數

async 函數基本使用

文法

async function 函數名(){
     
}      
  • 說明
    • 使用async修飾的函數就是async函數
    • async函數與普通函數區在于傳回值
    • async函數會隐匿傳回一個Promise對象,是以async函數調用後可以直接進行 .then() 操作 而函數内部的return則會将資料 傳遞給 then的回調函數
  • 原理
  • 可以了解async函數内有兩個return,隐式的return,顯示的return
  • 隐式的return傳回一個Promise對象,用于調用後面的thne 而顯示的return傳回資料給then内的回調函數

代碼示例

<script>
    /* 
        // 定義一個async函數
        async function fn() {
​
        }
​
        // async 會隐式傳回一個Promise對象
        console.log(fn());
​
        // 是以可以調用後面的 then(function(res){})
        fn().then(function (res) {
            console.log('then被調用了');
        })
    */
​
    async function fn() {
    // 顯示return一個資料
    return 100
    }
​
    fn().then(function(res){
        // 顯示return的資料會傳遞給 then的回調函數的參數res
        console.log(res);
    })
</script>      

async函數與await

  • async函數 與 await,配合使用可以簡化Promise的resolve資料的接收

文法格式

<script>
  async function fn(){
      let 變量 = await Promise對象
  }    
</script>      

文法格式

<script>
​
    // async函數 與 await,配合使用可以簡化Promise的resolve資料的接收
    // Promise resolve的資料 預設接收方式
    new Promise(function(resolve,reject){
        resolve('hello')
    }).then(function(res){          // promise内的resolve資料需要使用 .then進行接收
        console.log(res);
    })
​
    // async 與 await簡化接收方式
    async function fn1(){
        let res = await new Promise(function(resolve,reject){
            resolve('hello')
        })
        console.log(res);
    }
​
    fn1();
</script>      

async 異常捕獲

  • Promise有兩種結果:resolve結果,與reject結果
  • 上一小節中講解了使用async函數簡化了Promise執行個體的resolve結果,那麼reject的結果如何處理呢?
  • 解決辦法使用,使用try ... catch 代替 .catch()
   // 異常處理文法回顧
    try{
​
    }catch(e){
        
    }
    try ... catch 可以将try塊内發現的異常捕獲到catch塊内進行處理      

代碼示例

<script>
​
    // async可以使用 await 來處理Promise内部resolve的資料
    // 但 Promise的reject的情況如何來處理呢?
    
    // async 與 await簡化接收方式
    async function fn1(){
        try {
            let res = await new Promise(function(resolve,reject){
                if(Math.floor(Math.random()*10) % 2 == 0){
                    resolve('成功')
                }else{
                    reject('失敗')
                }
            })
            console.log(res);
        } catch (error) {
            console.log(error);
        }
    }
​
    fn1();
</script>      
  • 小結重點:
  • async函數裡的錯誤(promise執行個體reject的錯誤)可以使用什麼捕獲?
    try ... catch 是通用的異常處理文法,所有的異常都可以捕獲 而.catch隻能捕獲 Promise的異常

繼續閱讀