前言:該問題是因爲看到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 新建後當即執行,因此首先輸出的是Promise
。ui
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的狀態狀況而被依次調用。