首页 > 其他分享 >Promise基本用法

Promise基本用法

时间:2023-04-08 16:35:56浏览次数:39  
标签:基本 function console log 用法 xhr Promise 函数

JavaScript它的执行环境是单线程的,单线程就是任务只能一个一个的完成,这个任务完成之后才能执行下一个,它会阻塞其它任务。

而异步模式可以一起执行多个任务。常见的异步模式有定时器,接口调用和事件函数,Promise就是接口调用里面的一种方式,它是es6提供的一种异步解决方案。简单来说的话,Promis就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作的结果)。

JavaScript中常见的接口调用有

Ajex,fetch,promise,axios,async/await。

多次异步调用的依赖分析:

1.多次异步调用的结果,顺序可能不同步。

2.异步调用的结果如果存在依赖,则需要嵌套。

3.在ES5中,当进行多层嵌套回调时,会导致代码层次过多,很难进行维护和二次开发;而且会导致回调地狱的问题。ES6中的Promise 就可以解决这两个问题。

Promise介绍

Promis是异步编程的一种方案。从语法上来讲,Promise是一个对象,它可以获取异步操作的消息。

Promise的优点:

Promise对象,可以将异步操作以同步的流程表达出来,可以很好地解决回调的问题,避免层层嵌套的回调函数。语法非常简洁,Promise对象提供了简洁的API,使得控制异步操作更加容易。

Promise的基本用法

Es6规定Promise对象是一个构造函数,用来生成Promise实例

(1)使用new实例化一个Promise对象,Promise的构造函数中传递一个参数。这个参数是一个函数,改函数用来处理异步任务。

(2)并且会传入两个参数:reslove和reject,分别表示异步执行成功后的回调函数和异步执行失败后的回调函数;

(3)通过Promis.then()处理返回结果

在浏览器控制台上的输出如下

这里用timeout方法返回一个Promise实例,表示一段时间以后才会发生的结果。过了指定的时间(100ms)以后,Promise实例的状态变为resolved,就会触发then方法绑定的回调函数。

这里要知道Promise对象的三个状态

初始化状态(等待状态):pending

成功状态:fullfilled

失败状态:rejected

(1)当new Promise()执行之后,promise对象的状态会被初始化为pending,这个状态是初始化状态。new Promise()这行代码,括号里的内容是同步执行的。括号里定义一个function,function有两个参数:resolve和reject。如下:

如果请求成功了,则执行resolve(),此时,promise的状态会被自动修改为fullfilled。

如果请求失败了,则执行reject(),此时,promise的状态会被自动修改为rejected

(2)promise.then()方法,括号里面有两个参数,分别代表两个函数 function1 和 function2:

如果promise的状态为fullfilled(意思是:如果请求成功),则执行function1里的内容

如果promise的状态为rejected(意思是,如果请求失败),则执行function2里的内容

这里有一段代码

let promise = new Promise(function(resolve, reject) {

  console.log('Promise');

  resolve();

});

promise.then(function() {

  console.log('resolved.');

});

console.log('Hi!');

输出顺序是Promise,Hi!,resolved

上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。

这里简单说一下JavaScript中的宏任务和微任务

宏任务有Event Table、Event Queue,微任务有Event Queue

1.宏任务:包括整体代码script,setTimeout,setInterval、I/O、UI 交互事件、setImmediate(Node.js 环境);

2.微任务:Promise、MutaionObserver、process.nextTick(Node.js 环境)

注:new Promise中的代码会立即执行,then函数分发到微任务队列,process.nextTick分发到微任务队列Event Queue

任务进入执行栈----同步任务还是异步任务----同步的进入主线程,异步的进入Event Table并注册函数。当指定的事情完成时,Event Table会将这个函数移入Event Queue。主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。上述过程会不断重复,也就是常说的Event Loop(事件循环)。

这里有一个简单地宏任务微任务用例

setTimeout(function() {

    console.log('宏任务setTimeout');  //先遇到setTimeout,将其回调函数注册后分发到宏任务Event Queue

  //如果setTimeout设置时间,那它会先把函数放到宏任务Event Table,等时间到了再放入宏任务Event Queue里面

})

new Promise(function(resolve) {

    console.log('微任务promise');     //new Promise函数立即执行

    resolve();                     //必须resolve执行才能执行then

}).then(function() {

    console.log('微任务then');          //then函数分发到微任务Event Queue

})

console.log('主线程console');

//执行顺序结果: 微任务promise、主线程console、微任务then、宏任务setTimeout

Promise 的回调函数不是正常的异步任务,而是微任务(microtask)。它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。并且如果有多个Pomise的回调函数进入微任务队列,那么这些微任务会一起执行完才会去执行排队的宏任务。

事件循环,宏任务,微任务的关系如图所示

另外,resolve()和reject()这两个方法,是可以给promise.then()传递参数的。

案例如下:

在浏览器控制台输出结果为

基于Promise处理多次ajex请求(链式调用)

有了 promise之后,我们可以把多层嵌套调用按照线性的方式进行书写,非常优雅。

也就是说:Promise 可以把原本的多层嵌套调用改进为链式调用。

代码如下:

<script type="text/javascript">

function queryData(url) {

        var promise = new Promise((resolve, reject) => {

          var xhr = new XMLHttpRequest();

          xhr.onreadystatechange = function () {

            if (xhr.readyState != 4) return;

            if (xhr.readyState == 4 && xhr.status == 200) {

              // 处理正常情况

              resolve(xhr.responseText); // xhr.responseText 是从接口拿到的数据

            } else {

              // 处理异常情况

              reject("接口请求失败");

            }

          };

          xhr.responseType = "text"; // 设置返回的数据类型

          xhr.open("get", url);

          xhr.send(null); // 请求接口

        });

        return promise;

      }

      // 发送多个ajax请求并且保证顺序

      queryData("https://jsonplaceholder.typicode.com/posts")

        .then(

          (data1) => {

            console.log(JSON.stringify(data1));

            // 请求完接口1后,继续请求接口2

            return queryData("https://jsonplaceholder.typicode.com/posts");

          },

          (error1) => {

            console.log(error1);

          }

        )

        .then(

          (data2) => {

            console.log(JSON.stringify(data2));

            // 请求完接口2后,继续请求接口3

            return queryData("https://jsonplaceholder.typicode.com/posts");

          },

          (error2) => {

            console.log(error2);

          }

        )

        .then(

          (data3) => {

            // 获取接口3返回的数据

            console.log(JSON.stringify(data3));

          },

          (error3) => {

            console.log(error3);

          }

        );

</script>

这里的url我用的是jsonplaceholder上面的数据,在运行的时候xhr.responseType = "text";这一行代码原本是xhr.responseType = "json";因为我的浏览器不能正常读取json文件,所以讲json改成了text这样就能正常读取了,如果改了之后的代码无法正常运行或者读取不出文件,可以尝试把代码改回json。

可以清楚的看到这里数据是被请求了3次,做了三次输出

Return函数的返回值

Return后面的返回值有两种情况

第一种:返回一个新的Promise实例对象。返回的实例对象会调用下一个.then()

这里我引用的例子和上面的处理ajex请求基本一样

<script type="text/javascript">

            /*

              基于Promise发送Ajax请求

            */

            function queryData(url) {

                return new Promise((resolve, reject) => {

                    var xhr = new XMLHttpRequest();

                    xhr.onreadystatechange = function() {

                        if (xhr.readyState != 4) return;

                        if (xhr.readyState == 4 && xhr.status == 200) {

                            // 处理正常情况

                            resolve(xhr.responseText);

                        } else {

                            // 处理异常情况

                            reject('接口请求失败');

                        }

                    };

                    xhr.responseType = 'text'; // 设置返回的数据类型

                    xhr.open('get', url);

                    xhr.send(null); // 请求接口

                });

            }

            // 发送多个ajax请求并且保证顺序

            queryData(' https://jsonplaceholder.typicode.com/posts')

                .then(

                    data1 => {

                        console.log(JSON.stringify(data1));

                        return queryData(' https://jsonplaceholder.typicode.com/posts ');

                    },

                    error1 => {

                        console.log(error1);

                    }

                )

                .then(

                    data2 => {

                        console.log(JSON.stringify(data2));

                        // 这里的 return,返回的是 Promise 实例对象

                        return new Promise((resolve, reject) => {

                            resolve('qianguyihao');

                        });

                    },

                    error2 => {

                        console.log(error2);

                    }

                )

                .then(data3 => {

                    console.log(data3);

                });

        </script>

第二种:返回普通值,返回的普通值会直接传递给下一个then,通过then参数中的函数的参数接收该值

<script type="text/javascript">

            /*

              基于Promise发送Ajax请求

            */

            function queryData(url) {

                return new Promise((resolve, reject) => {

                    var xhr = new XMLHttpRequest();

                    xhr.onreadystatechange = function() {

                        if (xhr.readyState != 4) return;

                        if (xhr.readyState == 4 && xhr.status == 200) {

                            // 处理正常情况

                            resolve(xhr.responseText);

                        } else {

                            // 处理异常情况

                            reject('接口请求失败');

                        }

                    };

                    xhr.responseType = 'text'; // 设置返回的数据类型

                    xhr.open('get', url);

                    xhr.send(null); // 请求接口

                });

            }

            // 发送多个ajax请求并且保证顺序

            queryData(' https://jsonplaceholder.typicode.com/posts ')

                .then(

                    data1 => {

                        console.log(JSON.stringify(data1));

                        return queryData(' https://jsonplaceholder.typicode.com/posts ');

                    },

                    error1 => {

                        console.log(error1);

                    }

                )

                .then(

                    data2 => {

                        console.log(JSON.stringify(data2));

                        // 返回普通值

                        return 'qianguyihao';

                    },

                    error2 => {

                        console.log(error2);

                    }

                )

                /*

                    既然上方返回的是 普通值,那么,这里的 then 是谁来调用呢?

                    答案是:这里会产生一个新的 默认的 promise实例,来调用这里的then,确保可以继续进行链式操作。

                */

                .then(data3 => {

                    // 这里的 data3 接收的是 普通值 'qianguyihao'

                    console.log(data3);

                });

        </script>

Promise常用的API:实例方法

1.Promise.prototype.then()

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

 

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

getJSON("/post/1.json").then(function(post) {

  return getJSON(post.commentURL);

}).then(function (comments) {

  console.log("resolved: ", comments);

}, function (err){

  console.log("rejected: ", err);

});

上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数。

2. Promise.prototype.catch()

Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

getJSON('/posts.json').then(function(posts) {

  // ...

}).catch(function(error) {

  // 处理 getJSON 和 前一个回调函数运行时发生的错误

  console.log('发生错误!', error);

});

上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。

getJSON('/post/1.json').then(function(post) {

  return getJSON(post.commentURL);

}).then(function(comments) {

  // some code

}).catch(function(error) {

  // 处理前面三个Promise产生的错误

});

上面代码中,一共有三个 Promise 对象:一个由getJSON()产生,两个由then()产生。它们之中任何一个抛出的错误,都会被最后一个catch()捕获。

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

不管是then()方法还是catch()方法,都返回了一个新的Promise对象

3. Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。

不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

下面是一个例子,包含了三种实例方法

<script>

            function queryData() {

                return new Promise((resolve, reject) => {

                    setTimeout(function() {

                        var data = { retCode: 0, msg: 'qianguyihao' }; // 接口返回的数据

                        if (data.retCode == 0) {

                            // 接口请求成功时调用

                            resolve(data);

                        } else {

                            // 接口请求失败时调用

                            reject({ retCode: -1, msg: 'network error' });

                        }

                    }, 100);

                });

            }

            queryData()

                .then(data => {

                    // 从 resolve 获取正常结果

                    console.log('接口请求成功时,走这里');

                    console.log(data);

                })

                .catch(data => {

                    // 从 reject 获取异常结果

                    console.log('接口请求失败时,走这里');

                    console.log(data);

                })

                .finally(() => {

                    console.log('无论接口请求成功与否,都会走这里');

                });

        </script>

Promise 的常用API:对象方法

1.Promise.all():并发处理多个异步任务,所有任务都执行成功,才能得到结果。

const p = Promise.all([p1, p2, p3]);

p的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

2.Promise.race(iterable): 并发处理多个异步任务,只要有一个任务执行成功,就能得到结果。

const p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

3. Promise.allSettled()

用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

4. Promise.any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

 async/await的作用与用法

什么是async、await?

async基本用法

async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

下面是一个例子。

async function getStockPriceByName(name) {

  var symbol = await getStockSymbol(name);

  var stockPrice = await getStockPrice(symbol);

  return stockPrice;

}

getStockPriceByName('goog').then(function (result) {

  console.log(result);

});

上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。

async 函数有多种使用形式。

// 函数声明

async function foo() {}

// 函数表达式

const foo = async function () {};

// 对象的方法

let obj = { async foo() {} };

obj.foo().then(...)

// Class 的方法

class Storage {

  constructor() {

    this.cachePromise = caches.open('avatars');

  }

  async getAvatar(name) {

    const cache = await this.cachePromise;

    return cache.match(`/avatars/${name}.jpg`);

  }

}

const storage = new Storage();

storage.getAvatar('jake').then(…);

// 箭头函数

const foo = async () => {};

async函数返回一个 Promise 对象。

async函数内部return语句返回的值,会成为then方法回调函数的参数。

async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到。

Promise 对象的状态变化

async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

 await

正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。

await命令后面的 Promise 对象如果变为reject状态,则reject的参数会被catch方法的回调函数接收到。

只要一个await语句后面的 Promise 变为reject,那么整个async函数都会中断执行。

有时,我们希望即使前一个异步操作失败,也不要中断后面的异步操作。这时可以将第一个await放在try...catch结构里面,这样不管这个异步操作是否成功,第二个await都会执行。

另一种方法是await后面的 Promise 对象再跟一个catch方法,处理前面可能出现的错误。

错误处理

如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。

防止出错的方法,也是将其放在try...catch代码块之中

如果有多个await命令,可以统一放在try...catch结构中。

使用注意点

第一点,  前面已经说过,await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

第二点,  多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

第三点,  await命令只能用在async函数之中,如果用在普通函数,就会报错。

async 函数的实现原理

async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。

与其他异步处理方法的比较

我们通过一个例子,来看 async 函数与 Promise的比较。

假定某个 DOM 元素上面,部署了一系列的动画,前一个动画结束,才能开始后一个。如果当中有一个动画出错,就不再往下执行,返回上一个成功执行的动画的返回值。

首先是 Promise 的写法。

function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值

  var ret = null;

  // 新建一个空的Promise

  var p = Promise.resolve();

 

  // 使用then方法,添加所有动画

  for(var anim of animations) {

    p = p.then(function(val) {

      ret = val;

      return anim(elem);

    });

  }

 

  // 返回一个部署了错误捕捉机制的Promise

  return p.catch(function(e) {

    /* 忽略错误,继续执行 */

  }).then(function() {

    return ret;

  });

}

虽然 Promise 的写法比回调函数的写法大大改进,但是一眼看上去,代码完全都是 Promise 的 API(then、catch等等),操作本身的语义反而不容易看出来。

async 函数的写法。

async function chainAnimationsAsync(elem, animations) {

  var ret = null;

  try {

    for(var anim of animations) {

      ret = await anim(elem);

    }

  } catch(e) {

    /* 忽略错误,继续执行 */

  }

  return ret;

}

可以看到Async函数的实现最简洁,最符合语义,几乎没有语义不相关的代码。

 

参考:

https://www.cnblogs.com/sybboy/p/6420812.html

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await

https://blog.csdn.net/weixin_44801790/article/details/126345725

https://www.cnblogs.com/qianguyihao/p/12660393.html

标签:基本,function,console,log,用法,xhr,Promise,函数
From: https://www.cnblogs.com/dengke1126/p/17298727.html

相关文章

  • 03. Jenkins - Groovy(基本语法)
    GroovyGroovy是一种基于Java平台的面向对象语言。在JenkinsPipeline中通常用它来增强Pipeline的功能。详细的语法可以参考:https://www.w3cschool.cn/groovy/groovy_overview.html字符串(String)//定义全局变量num=1job_name="Devops-demo-test"pipeline{......
  • python中shutil和shutil库的用法
    一、shutil目录和文件操作Pythonshutil库提供了对文件和目录复制、移动、删除、压缩、解压等操作。1.复制文件或目录shutil.copy(src,dst):复制文件或目录shutil.copyfile(src,dst):复制文件,src和dst只能是文件shutil.copytree(src,dst,dirs_exist_ok=False):复制目录,默......
  • ProtoBuf 基本使用
    一、是什么ProtocolBuffers,是Google公司开发的一种数据描述语言,是一种平台无关、语言无关、可扩展且类似于XML能够将结构化数据序列化,可用于数据存储、通信协议等方面。二、为什么更简单数据描述文件只需原来的1/10至1/3解析速度是原来的20倍至100倍减少了二义性生成了更......
  • 大型互联网系统技术架构设计的4个基本要素
    作为一名大厂SRE,对什么是好产品(技术架构角度)有深刻的感悟。一个好产品的技术架构不仅在优秀的代码本身,更体现在后期的易运维性、可扩展性、高可用性上。随着用户体量、产品功能、IaaS、PaaS的变化甚至员工的离职,随时需要动态调整架构改变策略来应对各种问题,而这些场景都是对技术架......
  • 逍遥自在学C语言 | 位运算符的基础用法
    前言一、人物简介第一位闪亮登场,有请今后会一直教我们C语言的老师——自在。第二位上场的是和我们一起学习的小白程序猿——逍遥。二、构成和表达方式位运算符是一组用于在二进制数之间进行操作的运算符运算符名称示例&位与a&&b|位或a|b......
  • Python 日期和时间用法超强总结
    Python中处理日期和时间的模块Python提供了time和datetime模块,可以帮助我们轻松获取和修改日期和时间,下面让我们来逐一了解一下。time模块该模块包括使用时间执行各种操作所需的所有与时间相关的功能,它还允许我们访问多种用途所需的时钟类型。内置函数:请看下表,它描述了时......
  • C# ?用法
    1、可空类型修饰符(?),如:int?x=null;//可空类型默认值都是null,而值类型如int默认值为0、bool默认值为false等bool?result=true;2、三元运算符(?,如:boolf=false;returnf==true?1:0;如果f为true则返回1,否则返回03、空合并运算符(??)如:a??b当a为null时则返回b,a不为null时......
  • Python中排序函数sorted的用法
    Python中有两个排序函数:sorted与sort其中,sorted的用法与c++中的sort是基本一样的本文只介绍sorted用法sorted返回的是一个新的迭代对象,一般默认返回一个list如:对tensor进行排序,返回了一个list我们一般建议直接对list进行排序这样得到的最终还是list如:自定义排序......
  • Markdown 基本使用
    目录一、软件Typora二、Markdown基本知识1.Markdown介绍2.Markdown常用语法标题字体序列引用代码框上、下划线和上、下标一、软件TyporaTypora是一款免费轻便简洁的Markdown编辑器,支持即时渲染技术,这也是与其他Markdown编辑器最显著的区别。即时渲染,也就是Markdown......
  • 初次接触女孩的指南 – 基本注意事项
    初次接触女孩的指南–基本注意事项一个害羞的男人如何接近女孩以获得友谊或长期关系的指南。做什么和不做什么。(仅适用于18岁以上)课程英文名:FirstTimersGuidetoApproachAGirl-BasicDo's&Don'ts此视频教程共nan,中英双语字幕,画质清晰无水印,源码附件全![](https:/......