结合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的具体用法:

看到一个案例:任务执行顺序问题:(中间插曲)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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")  是同步任务,放置在主程序中,当主程序中的执行完,才会去查看任务队列

执行结果:

1
2
3
// Promise
// Hi!
// resolved<br><br><strong>继续介绍then用法:<br></strong>

  Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过,then方法的第一个参数是resolved状态的回调函数,第二个参数(可选)是rejected状态的回调函数。

  then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法

  这句话跟fetch的用法是一样的由于then的返回值是一个promise实例,可以采用的是链式写法,

1
还是但是fetch和promise还是没啥关系?<br>当我又看到一个案例:为啥getJSON()这个可以使用then,应该是new promise才可以使用then吗?我才明白getJSON是封装的一个函数,返回值是new promise,所以执行getJSON()可以使用then的方法
getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

 

1
到现在才明白fetch()其实就是封装了的promise的函数,返回值是promise的实例,所以才能调用then用法,所以说,fetch()其实就是promise的实例。回到最初的疑问?

1. fetch为什么可以使用then?(个人理解then方法是定义在原型对象Promise.prototype上的)

2. 为什么使用两次then才能取出数据?(重点疑惑是这里,疑惑第二个then没有进行其他操作,只是将上一个then的返回值进行输出,就可以获取到arr.txt的数据)

1
<strong>为什么使用两次then才能正常取出数据?我将最初的案例运行:查看第一个then的返回值是啥?<br></strong>
复制代码
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);    
        });
            
    };  
复制代码

 

1
<strong>解释两次then用法:<br></strong><br><strong>第一次then用法</strong>:then是根据promise的状态变化而执行的回调函数,promise的状态变化由resolve()函数决定(取到数据执行resolve),then的参数为resolve函数传递出来的数据,<br>直接输出res是一个对象不是我们需要的数据,使用res.json()或者res.test()获取到我们需要的数据。<br>res.json()/res.text()获取到的是一个新的promise实例,arr.txt的值在[[[PromiseValue]]里面,但是直接取是取不出来的。没有方法取出来,<br>Promise的设计文档中说了,[[PromiseValue]]是个内部变量,外部无法得到,只能在then中获取。所以就会用到第二次then了
1
<strong>第二次then用法:就是怎么将</strong>[[[PromiseValue]]里面的数据取出来
1
现在就重点理解下[[[PromiseValue]]这个怎么获取到的?<br>代码中的resolve()就是说明resolve内部是怎么运行的,改变promise的状态,给PromiseValue复制,
复制代码
/* 用于描述思维的代码 */
executor(resolve, reject) {
    ...
    resolve(value);
    ...
}
...
resolve(value) {
    PromiseStatus = 'fulfilled';
    PromiseValue = value;
    ...
    // 接着调用回调链中的回调函数
}
复制代码
1
<strong>这句话解决了第二个then的用法:<br></strong>onFulfilled(value)和onRejected(reason):<strong>参数value和reason的实参都是PromiseValue</strong>。这句话是说then的回调函数参数使用的都是<strong>PromiseValue,</strong>所以直接输出就会获取到PromiseValue的值<br><br><strong>这里有一点值得注意:第一个then 的return返回值是一个promise实例对象,所以回调链转交给了新的实例对象,第二个then的回调函数参数为为</strong><strong>PromiseValue的值,当返回值不是对象时,返回值是数据类型时,会将该返回值</strong>
1
<strong>赋值给PromiseValue,供下次的then函数使用<br></strong>
1
如果onFulfilled(value)和onRejected(reason)这两个回调函数中return返回值不是一个Promise的对象,(then)
1
那么这个返回值会被赋给PromiseValue,并在下一个then()的onFulfilled(value)和onRejected(reason)中做为实参使用。
1
但如果这个返回值是一个Promise的对象,那么剩下的由then()构造的回调链会转交给新的Promise对象并完成调用。
1
<strong>回调链是啥??<br><strong>then(onFulfilled, onRejected):</strong>这个方法实际上是把onFulfilled()函数和onRejected()函数添加到Promise对象的回调链中。<br>回调链就像一个由函数组构成的队列,每一组函数都是由至少一个函数构成(onFulfilled() 或者 onRejected() 或者 onFulfilled() 和 onRejected())。<br>当resolve()或者reject()方法执行的时候,回调链中的回调函数会根据PromiseStatus的状态情况而被依次调用。<br></strong>
1
<em id="__mceDel"><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br></em>
posted @   maxlove  阅读(17013)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示