JS Promise axios 请求结果后面的.then() 是什么意思

目录
  • Promise 对象
    • Promise 对象的状态
    • 回调函数
    • Promise.then() 绑定回调函数
  • 使用 Promise:链式调用
    • 链式调用的实现
    • 错误处理
    • 常见错误
  • 创建 Promise 对象
  • Promise 其他静态方法
    • 创建已决议的 Promise 对象
    • 多个 Promise 对象
  • 结语&参考文献

Promise 是JS中一种处理异步操作的机制,在现在的前端代码中使用频率很高。Promise 这个词可能有点眼生,但你肯定见过 axios.get(...).then(res => {...});用于异步请求的 axios 返回的就是一个 Promise 对象。

平时一直在代码中 .then() .catch() 地写来写去,终于决定要认真学一学这个 Promise 到底是怎么回事,希望这篇学习笔记也能帮到你。

Promise 对象

一个 Promise 对象表示一个异步操作的执行结果,包括状态(成功/失败)和值(成功返回值/错误原因)。一个 Promise 在创建的时候异步操作可能还没执行完成,通过持有这个 Promise 对象,可以在未来异步操作完成的时候对结果进行相应操作。

Promise 对象的状态

这里有几个常用的名词,用来表示 Promise 对象当前的具体状态:

Pending 待定:刚创建时的初始状态,还没有确定执行结果

Fulfilled 已兑现:异步操作执行成功,并返回一个值

Rejected 已拒绝:异步操作执行失败,并返回一个错误原因

Settled 已敲定 / Resolved 已决议:“待定”状态的反面,都表示异步操作已经执行完成,即已兑现已拒绝

回调函数

如果完全不关心异步操作的执行结果,那就把它放在那自己执行就可以了;但通常情况下我们总是要对操作执行的结果进行后续处理的,例如更改页面上的数据显示、错误处理等。但由于异步操作不知道什么时候可以执行完成,就出现了“回调函数”的概念,意思就是等到异步操作处理结束了,再回过头来调用这个函数来对执行结果进行处理。

传统做法是,在执行异步操作的时候就把回调函数作为参数传进去,比如最常见的:

setTimeout(function(){
    console.log("成功!");
}, 250);

setTimeout() 函数是最常见的异步函数之一,众所周知它的作用就是在指定时间后执行指定代码。仔细看就会发现,setTimeout()函数接收两个参数,第二个参数是等待时间,而第一个参数就是回调函数,即等待指定的时间之后要回来调用这个函数。

很显然这种传参的做法有很多不方便的地方,比如把对结果的后续处理和异步操作本身耦合在了一起,以及著名的回调地狱:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

Promise.then() 绑定回调函数

有了 Promise 之后,就能把回调和异步操作本身分开了。无论一个 Promise 对象当前是否已经执行完毕,我们都能在它上面绑定回调函数,并且保证回调函数被执行;这就是喜闻乐见的 then() 方法。

p.then(onFulfilled[, onRejected]);
p.then(value => {
  // fulfillment
}, reason => {
  // rejection
});

then() 方法的语法很简单,有两个可选参数,分别代表当 Promise 的状态变为成功(fulfilled)和失败(rejected)时所使用的回调函数。

如果只想绑定 onRejected(即失败时的错误处理函数),下面两种写法完全等价,第二种是第一种的简写形式。

p.then(null, failureCallback);
p.catch(failureCallback);

使用 Promise:链式调用

如果只是用 then 来绑定回调函数,那并不能解决回调地狱的问题。然而很妙的地方来了:Promise 支持链式调用

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

链式调用的实现

能做到链式调用的魔法来自 then() 方法:它会在执行相应的回调函数之后,返回一个新的 Promise 对象,并且插入 Promise 链的当前位置

这里稍微有点绕,容易把回调函数等同于 then() 方法本身。实际上成功/失败的回调函数只是 then() 的参数而已;而实际执行 then() 的时候,它会先根据 promise 的状态调用相应的回调函数,再根据回调函数的执行结果生成一个新的 Promise 对象并返回;具体的对应规则如下:

回调函数执行情况 then() 返回的 Promise 对象
返回值 return x; fulfilled 状态,参数为 x
直接返回 return; / 无 return 语句 fulfilled 状态,参数为 undefined
抛出错误 throw err; rejected 状态,参数为 err
返回已决议的 Promise 状态和参数与返回的 Promise 一致
返回未定的 Promise 未定的 Promise,回调参数与返回的相同

下面这个例子中,初始 Promise 的状态为已拒绝,然后第一个 then() 调用了绑定的 onRejected,返回了状态为 fulfilled 的新 Promise 对象,并传递给了链中的下一个 then():

Promise.reject()
  .then(() => 99, () => 42) // 调用 onRejected(return 42;),表格中的第一种情况
  .then(solution => console.log('Resolved with ' + solution)); // Resolved with 42

同时,你可能还记得 then() 的参数定义,两个回调函数都是可选的;如果没有传入对应的回调函数,then() 会直接把原 promise 的终态返回,不做额外处理。

错误处理

遇到异常抛出(被拒绝的 promise)时,浏览器会顺着 Promise 链寻找下一个 onRejected 回调函数(经常被简写为 .catch()),并跳过中间的 onFulfilled 回调函数。这种执行逻辑与同步代码中的 try-catch 执行过程非常相似:

// 异步 Promise
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);

// 同步
try {
  let result = syncDoSomething();
  let newResult = syncDoSomethingElse(result);
  let finalResult = syncDoThirdThing(newResult);
  console.log(`Got the final result: ${finalResult}`);
} catch(error) {
  failureCallback(error);
}

一个具体的例子:

Promise.resolve()
.then(() => {
    throw new Error('出错');
    console.log('a');
})
.then(() => {
    console.log('b');
})
.catch(() => {
    console.log('c');
})
.then(() => {
    console.log('d');
});

// 输出结果:
// "c"
// "d"

常见错误

doSomething().then(function(result) {
  doSomethingElse(result) // 没有返回 Promise 以及没有必要的嵌套 Promise
  .then(newResult => doThirdThing(newResult));
}).then(() => doFourthThing());
// 最后,是没有使用 catch 终止 Promise 调用链,可能导致没有捕获的异常

上面这个例子在 Promise 中进行了嵌套,但没有将嵌套的 Promise 对象返回,因此doFourthThing() 不会等待 doSomethingElse()或 doThirdThing() 完成,而是并行运行;并且如果有传入参数,接收到的会是 undefined 而不是 doThirdThing() 的执行结果。

正确的写法应该是:

注:箭头函数 () => x 是 () => { return x; } 的简写,即返回了新的 Promise 对象

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(newResult => doThirdThing(newResult))
.then(() => doFourthThing())
.catch(error => console.log(error));

创建 Promise 对象

如果要执行的异步操作没有返回 Promise 对象,可以用 new 和构造器创建自己的 promise。构造器的两个参数的作用是在异步操作成功/失败时,转换 Promise 对象的状态并传递对应参数。

const myFirstPromise = new Promise((resolve, reject) => {
  // 做一些异步操作,最终会调用下面两者之一:
  // resolve(someValue); // fulfilled
  // reject("failure reason"); // rejected
});

// 一个例子
function myAsyncFunction(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url);
    xhr.onload = () => resolve(xhr.responseText);
    xhr.onerror = () => reject(xhr.statusText);
    xhr.send();
  });
};

Promise 其他静态方法

创建已决议的 Promise 对象

Promise.resolve(value) 和 Promise.reject(reason) 方法分别返回一个已经成功/失败的 Promise 对象。

const p1 = new Promise((resolve, reject) => {
  resolve();
});
const p2 = Promise.resolve();

如果 resolve(value) 的参数是带有 then() 方法的 Promise 对象,函数将返回其自带 then() 方法的执行结果;如果参数为空或是基本类型,返回的Promise对象与在 then() 方法中 return 对应值的结果一致,参见上文表格。基于这样的特性, resolve(value) 方法可以用于将不确定是否为 Promise 对象的 value 值统一为 Promise。

多个 Promise 对象

Promise.all(iterable)

  • 参数列表中的所有的 promises 都成功时,返回一个 fulfilled 的 Promise 对象,参数值是所有 promises 成功返回值的列表(顺序不变)
  • 如果任何一个 promise 失败,立即返回一个 rejected 的 Promise 对象,参数是这个失败 promise 的错误信息
Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

Promise.allSettled(iterable)列表中所有 promises 都已敲定后返回一个promise,并带有一个对象数组,对应每个promise 的结果。

Promise.any(iterable)当列表中的任意一个 promise 成功时,立即返回这个 promise 的值。

Promise.race(iterable)当列表中任意一个 promise 成功或失败时,立即返回该 promise 对象的执行结果。

一个综合例子(使用 setTimeout 模拟异步操作):

// 创造一个状态为 fulfilled,参数为"foo"的 Promise 对象
Promise.resolve("foo")
  .then(function(string) {	// string: "foo"
    // 返回状态为 fulfilled,参数为"foobar"的对象
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
  .then(function(string) {	// string: "foobar"
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    // 返回值"foobar"
    return string;
  })
  .then(function(string) {	// string: "foobar"
    console.log("Last Then");
    console.log(string);
  });

// 输出结果:
// Last Then
// foobar
// foobarbaz(由第二个 then 中的 setTimeout 输出)

结语&参考文献

以上是阅读学习了 MDN 文档后个人总结的学习笔记,可能存在错误和疏漏,欢迎指正与讨论!

MDN-Promise

MDN-使用Promise

MDN-Promise.then()

到此这篇关于JS Promise axios 请求结果后面的.then() 是什么意思 的文章就介绍到这了,更多相关JS Promise axios  .then() 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaScript axios安装与封装案例详解

    1.下载axios插件 cnpm install axios -S 2.在main.js引入axios import axios from 'axios' Vue.prototype.$http = axios 3.创建axios实例 let service = axios.create({ baseURL: baseUrl, // url = base api url + request url withCredentials: true, // send cookies when cross

  • JS中封装axios来管控api的2种方式

    前言:我们在开发项目的时候,往往要处理大量的接口.并且在测试环境 开发环境 生产环境使用的接口baseurl都不一样 这时候如果在开发环境完成之后切换每一个接口的baseurl会变的非常的麻烦,(要去每一个发出请求的页面都要去修改地址) 所以为了更好的管控这些api,我们需要自己封装一个axios定义统一的接口baseurl 这样在环境的切换的时候更好的管控和修改.话不多说上代码!!! 自己创建一个api文件夹 即可 import axios from 'axios' 为了处理java字符串问题

  • JS Promise axios 请求结果后面的.then() 是什么意思

    目录 Promise 对象 Promise 对象的状态 回调函数 Promise.then() 绑定回调函数 使用 Promise:链式调用 链式调用的实现 错误处理 常见错误 创建 Promise 对象 Promise 其他静态方法 创建已决议的 Promise 对象 多个 Promise 对象 结语&参考文献 Promise 是JS中一种处理异步操作的机制,在现在的前端代码中使用频率很高.Promise 这个词可能有点眼生,但你肯定见过 axios.get(...).then(res =>

  • JS promise 的回调和 setTimeout 的回调到底谁先执行

    目录 任务 VS 微任务 执行过程 案例分析 结语 & 参考资料 首先提一个小问题:运行下面这段 JS 代码后控制台的输出是什么? console.log("script start"); setTimeout(function () { console.log("setTimeout1"); }, 0); new Promise((resolve, reject) => { setTimeout(function () { console.log(&

  • Js 代码中,ajax请求地址后加随机数防止浏览器缓存的原因

    看到别人写的JS,ajax请求地址后加随机参数,比如XXXX?t= + new Date().getTime(). 一开始搞不懂为什么,网上查了资料,原因是防止浏览器缓存. 浏览器为了提高用户访问同一页面的速度,会对页面数据进行缓存.当url请求地址不变时, 有时候会导致浏览器不发送请求,直接从缓存中读取之前的数据. 如果数据改变了,而没加随机数,读取的数据会跟之前一样. 加上随机数,就是欺骗浏览器url改变了,会每次都向服务器发送请求而不去读缓存

  • vue axios请求成功却进入catch的原因分析

    问题:axios返回200状态码(即请求成功)却走进了catch里面 原因: 1.当axios请求完成后走的时then的代码块,如果then代码块中存在错误代码信息,这时就会进入catch中抛出异常(注意:此时控制台并不会报错,因为错误被catch捕获了) 2.axios是异步发起,若发起后页面刷新,那么就会丢失当前进程,导致接收不到.例如 form表单,点击按钮提交后,表单会刷新 补充知识:axios用catch的写法与不使用catch有什么区别? 官网上的写法: axios.post(url

  • Vue Promise的axios请求封装详解

    现在应该大部分公司都是前后端分离了.so,数据请求的封装还是必须的. 为了实现向ios中block封装请求的异步的效果,我采用JavaScript中promise这个对象. var p1 = New promise((resolve,reject)=>{ var timeOut = Math.random() * 2; log('set timeout to: ' + timeOut + ' seconds.'); setTimeout(function () { if (timeOut <

  • nuxt+axios实现打包后动态修改请求地址的方法

    需求:把请求地址等一些配置暴露出来以便打包后动态修改,而不需要重新打包编译. 这样也是挺不错的,当一个项目需要部署到几个不同的服务器上时候,就不用说每次都要修改后再重新打包.功能不变时,单单是修改一下请求地址省了不少功夫. 1.开始 把需要动态修改的配置写在一个配置json文件里面.该配置文件可以放在static目录下: 静态文件目录:静态文件目录 static 用于存放应用的静态文件,此类文件不会被 Nuxt.js 调用 Webpack 进行构建编译处理. 服务器启动的时候,该目录下的文件会映

  • js实现axios限制请求队列

    目录 背景是: 会造成什么情况呢? 背景是: 在实际开发中,可能会遇到网络问题或者查询量比较大的情况,上一个请求还没有完成,用户就发起了下一个请求. 会造成什么情况呢? 但是同一个请求多次发送到服务器,无疑是对服务器的一种压力,所以需要在已经优化服务器过查询速度后,以及用户网络情况比较差的条件下,在前端进行请求限制. axios 自带的cancelToken可以帮我们实现这个需求,并且提供给了我们一个现成的api axios.CancelToken ,这是一个返回值是带有请求信息的回调函数,我们

  • 封装 axios+promise通用请求函数操作

    我就废话不多说了,大家还是直接看代码吧~ import axios from "axios"; import baseUrl from "../../setBaseUrl"; axios.defaults.baseURL = baseUrl; import { Loading, Message } from "element-ui"; const loadingOptions = { lock: true, text: "拼命加载中&q

  • node.js通过axios实现网络请求的方法

    1.使用Npm 下载axios npm install --save axios var update_url = axios.create({ baseURL:'debug url' }); update_url.get('/debug url').then(function (response){ //response 就是请求url 返回的内容 } 上述的方法请求文件时候,body的默认格式不是form-data.因此我们需要请求的数据格式为form-data的时候,需要使用下面的库 re

  • 解决java使用axios.js的post请求后台时无法接收到入参的问题

    使用vue有一段时间了,下面是我在Java环境下使用vue+axios的时候遇到的一个坑,在这分享给大家,如有不正确或者疑惑的地方可以给我留言. 1.在使用异步请求后台时,由于官方不在更新vue-resource,推荐使用axios,因此在使用的时候难免会遇到各种问题.目前遇到最大的问题是在使用axios.post的请求向Java后端传入入参时,后端无法接收到参数.在这里主要处理移动端浏览器兼容的问题. 在这里我提供了两种解决办法: 一.URLSearchParams.append()方法 由于

随机推荐