結合promise對原生fetch的兩個then用法理解

前言:該問題是因爲看到fetch的then方法的使用,產生的疑問,在深刻了解並記錄對promise的我的理解html

首先看一下fetch請求使用案例:es6

案例效果:點擊頁面按鈕,請求當前目錄下的arr.txt裏面的內容json

疑問地方:數組

1. fetch爲何可使用then?(我的理解then方法是定義在原型對象Promise.prototype上的promise

2. 爲何使用兩次then才能取出數據?(重點疑惑是這裏,疑惑第二個then沒有進行其餘操做,只是將上一個then的返回值進行輸出,就能夠獲取到arr.txt的數據異步

let oBtn = document.getElementById("btn1"); oBtn.onclick = function(){ let url = "arr.txt"; //let url = "json.txt";
fetch(url).then(res=>{ /* res.text 返回的是一個純文本 是一個promise對象 res.json 返回的是一個對象(json/array) 是一個promise對象 response是隻能被讀取一次的,console.log取一次,return取一次,會報錯 */ let resdata = res.json(); console.log(0,resdata); //打印:[[PromiseStatus]]: "resolved" //return res.text(); return resdata; //返回值是一個新的promise對象,狀態爲resolved,因此執行then
}).then(data=>{ //上一個then返回值是Promise對象(即有異步操做),等待該Promise對象的狀態發生變化,then纔會被調用 console.log(1,data)},data2=>{ console.log(2,data2)} ).catch(err=>{ console.log(4,err); }); };

 因此去查詢了then的用法,查詢到阮一峯寫了關於promise的文章http://es6.ruanyifeng.com/?search=fecth&x=0&y=0#docs/promise,裏面介紹到promise和then的具體用法:函數

看到一個案例:任務執行順序問題:(中間插曲)post

let promise = new Promise(function(resolve, reject) {
  console.log('Promise');
  resolve();
});

promise.then(function() {
  console.log('resolved.');
});

console.log('Hi!');

// Promise
// Hi!
// resolved

解釋:fetch

1.  Promise 新建後當即執行,因此首先輸出的是Promiseui

2. 當請求到數據後,執行resolve方法,改變promise狀態爲resolved,使then方法執行第一個回調函數,第一個函數參數爲resolve傳遞出來的數據,將在當前腳本全部同步任務執行完纔會執行,因此resolved最後輸出。爲啥最後執行呢?

  我又去查詢下JavaScript 運行機制詳解:也是理解與阮一峯的:

  理解出一條:先執行主線程(同步任務放置在主線程),主線程執行完,系統去讀取任務隊列中(異步任務放置在任務隊列),js的運行機制是這樣設定的。嗯,沒毛病

 3. 意思就是resolve是異步任務,放置在任務隊列中,console.log("HI")  是同步任務,放置在主程序中,當主程序中的執行完,纔會去查看任務隊列

執行結果:

// Promise
// Hi!
// resolved

繼續介紹then用法:

  Promise 實例具備then方法,也就是說,then方法是定義在原型對象Promise.prototype上的。它的做用是爲 Promise 實例添加狀態改變時的回調函數。前面說過,then方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。

  then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。所以能夠採用鏈式寫法,即then方法後面再調用另外一個then方法

  這句話跟fetch的用法是同樣的因爲then的返回值是一個promise實例,能夠採用的是鏈式寫法,

仍是可是fetch和promise仍是沒啥關係?
當我又看到一個案例:爲啥getJSON()這個可使用then,應該是new promise纔可使用then嗎?我才明白getJSON是封裝的一個函數,返回值是new promise,因此執行getJSON()可使用then的方法
getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

 

到如今才明白fetch()其實就是封裝了的promise的函數,返回值是promise的實例,因此才能調用then用法,因此說,fetch()其實就是promise的實例。
回到最初的疑問?

1. fetch爲何可使用then?(我的理解then方法是定義在原型對象Promise.prototype上的)

2. 爲何使用兩次then才能取出數據?(重點疑惑是這裏,疑惑第二個then沒有進行其餘操做,只是將上一個then的返回值進行輸出,就能夠獲取到arr.txt的數據)

爲何使用兩次then才能正常取出數據?我將最初的案例運行:查看第一個then的返回值是啥?
let oBtn = document.getElementById("btn1"); 
    oBtn.onclick = function(){
        let url = "arr.txt";
        //let url = "json.txt";

        fetch(url).then(res=>{
            /*
            res.text 返回的是一個純文本  是一個promise對象
            res.json 返回的是一個對象(json/array)  是一個promise對象
            response是隻能被讀取一次的,console.log取一次,return取一次,會報錯
            */
            let resdata = res.json();
            console.log(0,resdata);
            //打印:[[PromiseStatus]]: "resolved"
            //return res.text();
            return resdata; //返回值是一個新的promise對象,狀態爲resolved,因此執行then

         // Promise
         // __proto__
         //:
         //Promise
         //[[PromiseStatus]]:"resolved"
         //[[PromiseValue]]:Array[3]

        }).then(data=>{ //上一個then返回值是Promise對象(即有異步操做),等待該Promise對象的狀態發生變化,then纔會被調用
            console.log(1,data)},data2=>{
            console.log(2,data2)}    
        ).catch(err=>{
            console.log(4,err);    
        });
            
    };  

 

解釋兩次then用法:

第一次then用法:then是根據promise的狀態變化而執行的回調函數,promise的狀態變化由resolve()函數決定(取到數據執行resolve),then的參數爲resolve函數傳遞出來的數據,
直接輸出res是一個對象不是咱們須要的數據,使用res.json()或者res.test()獲取到咱們須要的數據。
res.json()/res.text()獲取到的是一個新的promise實例,arr.txt的值在[[[PromiseValue]]裏面,可是直接取是取不出來的。沒有方法取出來,
Promise的設計文檔中說了,[[PromiseValue]]是個內部變量,外部沒法獲得,只能在then中獲取。因此就會用到第二次then了
第二次then用法:就是怎麼將[[[PromiseValue]]裏面的數據取出來
如今就重點理解下[[[PromiseValue]]這個怎麼獲取到的?
代碼中的resolve()就是說明resolve內部是怎麼運行的,改變promise的狀態,給PromiseValue複製,
/* 用於描述思惟的代碼 */
executor(resolve, reject) {
    ...
    resolve(value);
    ...
}
...
resolve(value) {
    PromiseStatus = 'fulfilled';
    PromiseValue = value;
    ...
    // 接着調用回調鏈中的回調函數
}
這句話解決了第二個then的用法:
onFulfilled(value)和onRejected(reason):參數value和reason的實參都是PromiseValue。這句話是說then的回調函數參數使用的都是PromiseValue,因此直接輸出就會獲取到PromiseValue的值

這裏有一點值得注意:第一個then 的return返回值是一個promise實例對象,因此回調鏈轉交給了新的實例對象,第二個then的回調函數參數爲爲PromiseValue的值,當返回值不是對象時,返回值是數據類型時,會將該返回值
賦值給PromiseValue,供下次的then函數使用
若是onFulfilled(value)和onRejected(reason)這兩個回調函數中return返回值不是一個Promise的對象,(then)
那麼這個返回值會被賦給PromiseValue,並在下一個then()的onFulfilled(value)和onRejected(reason)中作爲實參使用。
但若是這個返回值是一個Promise的對象,那麼剩下的由then()構造的回調鏈會轉交給新的Promise對象並完成調用。
回調鏈是啥??
then(onFulfilled, onRejected):這個方法其實是把onFulfilled()函數和onRejected()函數添加到Promise對象的回調鏈中。回調鏈就像一個由函數組構成的隊列,每一組函數都是由至少一個函數構成(onFulfilled() 或者 onRejected() 或者 onFulfilled() 和 onRejected())。當resolve()或者reject()方法執行的時候,回調鏈中的回調函數會根據PromiseStatus的狀態狀況而被依次調用。
相關文章
相關標籤/搜索