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


前言:該問題是由於看到fetch的then方法的使用,產生的疑問,在深入了解並記錄對promise的個人理解

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

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

疑問地方:

1. fetch為什么可以使用then?(個人理解then方法是定義在原型對象Promise.prototype上的

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的具體用法:

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

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

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

console.log('Hi!');

// Promise
// Hi!
// resolved

解釋:

1.  Promise 新建后立即執行,所以首先輸出的是Promise

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的狀態情況而被依次調用。






















免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM