iis服务器助手广告广告
返回顶部
首页 > 资讯 > 前端开发 > html >如何上手Promise和aysnc/await
  • 587
分享到

如何上手Promise和aysnc/await

2024-04-02 19:04:59 587人浏览 八月长安
摘要

如何上手Promise和aysnc/await,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。单线程,就是指一次只能完成一件任务,如果有多个任务

如何上手Promise和aysnc/await,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

线程,就是指一次只能完成一件任务,如果有多个任务就必须排队等候,前面一个任务完成,再执行后面一个任务。这种“单线程”模式执行效率较低,任务耗时长。

为了解决这个问题,就有了异步模式,也叫异步编程

一、异步编程

所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,当第一段有了执行结果之后,再回过头执行第二段。

javascript采用异步编程原因有两点:

  • JavaScript是单线程。

  • 为了提高CPU的利用率。

在提高CPU的利用率的同时也提高了开发难度,尤其是在代码的可读性上。

那么异步存在的场景有:

  • fs 文件操作

require("fs").readFile("./index.html",(err,data)=>{})
$.get("/user",(data)=>{})

定时器

setTimeout(()=>{},2000)

二、Promise是什么

Promise理解

(1) 抽象表达

  • Promise 是一门新的技术(es6规范)

  • Promise是js中进行异步编程的新解决方案

(2) 具体表达

  • 从语法上说:Promise是一个构造函数

  • 从功能上说:Promise对象是用来封装一个异步操作并可以获取其成功/失败的结果值

为什么要使用Promise

(1) 指定回调函数的方式更加灵活

  • promise:启动异步任务=>返回promise对象=>给promise对象绑定回调函数

(2) 支持链式调用方式,可以解决回调地狱问题

  • 什么是回调地狱?

回调地狱就是回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调执行的条件

  • 回调地狱的缺点

不便于阅读

不便于异常处理

解决方法

Promise的状态

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. Promise必须拥有三种状态:pending、rejected、resolved

  3. 如果Promise的状态是pending时,它可以变成成功fulfilled或失败rejected

  4. 如果promise是成功状态,则它不能转换为任何状态,而且需要一个成功的值,并且这个值不能改变

  5. 如果promise是失败状态,则它不能转换成任何状态,而且需要一个失败的原因,并且这个值不能改变

Promise的状态改变

pending未决定的,指的是实例状态内置的属性

(1)pending变为resolved/fullfilled

(2)pending变为rejected

说明:Promise的状态改变只有两种,且一个Promise对象只能改变一次,无论失败还是成功都会得到一个结果输出,成功的结果一般是value,失败的结果一般是reason。

无论状态是成功还是失败,返回的都是promise。

Promise的值

实例对象中的另一个属性 [PromiseResult]保存着异步任务 [成功/失败] 的结果resolve/reject。

Promise的api

手写Promide中的api:

(1)promise构造函数 Promise(executor){}

  • executor:执行器(resolve,reject)=>{}

  • resolve:内部定义成功时我们需要调用的函数value=>{}

  • reject:内部定义失败时我们调用的函数  reason=>{}说明:executor会在Promise内部立即同步调用,异步操作在执行器中执行

(2)Promise.prototype.then方法:(onResolved,rejected)=>{}

  • onResolved函数:成功的回调函数value=>{}

  • rejected函数:失败的回调函数reason=>{}

说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的promise对象

(3)Promise.prototype.catch方法:(onRejected)=>{}

前三条是本文章中将要实现的手写代码,当然Promise还有其它的api接口。

(1)Promise.prototype.finally()方法

finally()方法用于指定不管 Promise  对象最后状态如何,都会执行的操作。不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。

promise .then(result => {···}) .catch(error => {···}) .finally(() => {···});

(2)Promise.all()方法

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。

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

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

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

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

(3)Promise.race()方法

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。

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

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

(4)Promise.allSettled()方法

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise  实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。

const promises = [   fetch('/api-1'),   fetch('/api-2'),   fetch('/api-3'), ];  await Promise.allSettled(promises); removeLoadingIndicator();

该方法返回的新的 Promise 实例,一旦结束,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,Promise  的监听函数接收到的参数是一个数组,每个成员对应一个传入Promise.allSettled()的 Promise 实例。

(5)Promise.any()方法

ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise  实例返回。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

(6)Promise.reject(reason)方法

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了'))  p.then(null, function (s) {   console.log(s) }); // 出错了

(7)Promise.resolve()方法 有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。

Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))

改变promsie状态和指定回调函数谁先谁后?

(1)都有可能,正常情况下是先指定回调函数再改变状态,但也可以先改变状态再指定回调函数。

(2)如何改变状态再指定回调?

  • 在执行器中直接调用resolve/reject

  • 延迟更长时间才进行调用then

(3)什么时候才能得到数据?

  • 如果先指定的回调,那当状态发生改变,回调函数就会调用,得到数据

  • 如果先改变的状态,那当指定回调时,回调函数就会进行调用,得到数据

示例:

let p = new Promise((resolve,reject)=>{     resolve("成功了");     reject("失败了"); });  p.then((value)=>{     console.log(value); },(reason)=>{     console.log(reason); })

Promise规范

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. "Promise"是一个具有then方法的对象或函数,其行为符合此规范。也就是说Promise是一个对象或函数

  3. "thenable"是一个具有then方法的对象或函数,也就是这个对象必须拥有then方法

  4. "value"是任何合法的js值(包括undefined或promise)

  5. promise中的异常需要使用throw语句进行抛出

  6. promise失败的时候需要给出失败的原因

then方法说明

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 一个promise必须要有一个then方法,而且可以访问promise最终的结果,成功或者失败的值

  3. then方法需要接收两个参数,onfulfilled和onrejected这两个参数是可选参数

  4. promise无论then方法是否执行完毕,只要promise状态变了,then中绑定的函数就会执行。

链式调用

Promise最大的优点就是可以进行链式调用,如果一个then方法返回一个普通值,这个值就会传递给下一次的then中,作为成功的结果。

如果返回的是一个promise,则会把promise的执行结果传递下去取决于这个promise的成功或失败。

如果返回的是一个报错,就会执行到下一个then的失败函数中。

三、手写Promise代码

面试经常考的手写Promise代码,可以仔细理解一下。

// 手写Promise // 首先定义一个构造函数,在创建Promise对象的时候会传递一个函数executor, // 这个函数会立即被调用,所以我们在Promise内部立即执行这个函数。 function Promise(executor){     // 用于保存promise的状态     this.status = "pending";     this.value;//初始值     this.reason;//初始原因     this.onResolvedCallbacks = [];//存放所有成功的回调函数     this.onRejectedCallbacks = [];//存放所有失败的回调函数     //定义resolve函数     const resolve = (value)=>{         if(this.status === "pending"){             this.status = "resolved";             this.value = value;             this.onResolvedCallbacks.forEach(function(fn){                 fn()             })         }     }     //定义reject函数     const reject = (reason)=>{         if(this.status === "pending"){             this.status = "rejected";             this.reason = reason;             this.onRejectedCallbacks.forEach(function(fn){                 fn()             })         }     }     executor(resolve,reject); }  Promise.prototype.then = function(onFulfilled,onRejected){      return new Promise((resolve,reject)=>{         //当Promise状态为resolved时         if(this.status === "resolved"){             try{                 resolve(onFulfilled(this.value))             }catch(error){                 reject(error)             }         }         //当Promise状态为rejected时         if(this.status === "rejected"){             try {                 resolve(onRejected(this.reason))             } catch (error) {                 reject(error)             }         }         //当Promise状态为pendding         if(this.status === "pending"){             this.onResolvedCallbacks.push(function(){                 try{                     resolve(onFulfilled(this.value))                 }catch(error){                     reject(error)                 }             });             this.onRejectedCallbacks.push(function(){                 try {                     resolve(onRejected(this.reason))                 } catch (error) {                     reject(error)                 }             });         }     }) }

升级版Promise:

class Promise{          constructor(executor){         this.executor = executor(this.resolve,this.reject);                  this.onResolvedCallbacks = [];//存放所有成功的回调函数         this.onRejectedCallbakcs = [];//存放所有失败的回调函数     }           // 用于存储相应的状态     status = "pending";     // 初始值     value;     // 初始原因     reason;          // executor在执行的时候会传入两个方法,一个是resolve,     // 一个reject,所以我们要创建这两个函数,而且需要把这两个函数传递给executor。     // 当我们成功或者失败的时候,执行onFulfilled和onRejected的函数,     // 也就是在resolve函数中和reject函数中分别循环执行对应的数组中的函数。     // 定义成功事件     resolve(value){         if(status === "pending"){             status = "resolved";             value = value;             this.onResolvedCallbacks.forEach(fn=>{fn()})         }     }     // 定义失败事件     reject(){         if(this.status === "pending"){             this.status = "rejected";             this.reason = reason;             this.onRejectedCallbakcs.forEach(fn=>{fn()});         }     }     // 这个时候当我们异步执行resolve方法时候,then中绑定的函数就会执行,并且绑定多个then的时候,多个方法都会执行。       // Promise的对象存在一个then方法,这个then方法里面会有两个参数,一个是成功的回调onFulfilled,     // 另一个是失败的回调onRejected,只要我们调用了resolve就会执行onFulfilled,调用了reject就会执行onRejected。     // 为了保证this不错乱,我们定义一个self存储this。当我们调用了resolve或reject的时候,需要让状态发生改变.     // 需要注意的是Promise的状态只可改变一次,所以我们要判断,只有当状态未发生改变时,才去改变状态。     then(onFulfilled,onRejected){         // 判断当前状态进行回调         if(this.status === "resolved"){             onFulfilled(self.value)         };         if(this.status === "rejected"){             onRejected(self.reason)         }         // 当状态还处于pending状态时         // 因为onFulfilled和onRejected在执行的时候需要传入对应的value值,所我们这里用一个函数包裹起来,将对应的值也传入进去。         if(this.status === "pending"){             this.onResolvedCallbacks.push(()=>{onFulfilled(this.value)});             this.onResolvedCallbacks.push(()=>{onRejected(this.reason)});         }     }  }

使用自己手写的Promise源码

let p = new Promise((resolve,reject)=>{     setTimeout(()=>{         resolve("成功了")     },1000) });  p.then(function(value){     return 123; }).then(value=>{     console.log("收到了成功的消息:",value); }).catch(error=>{     console.log(error); });  p.then(value=>{     console.log(value); })

四、Async/Await

async用来表示函数是异步的,定义的async函数返回值是一个promise对象,可以使用then方法添加回调函数。

await 可以理解为是 async wait 的简写。await 必须出现在 async  函数内部,不能单独使用。函数中只要使用await,则当前函数必须使用async修饰。

所以回调函数的终结者就是async/await。

async命令

  • async函数返回的是一个promise对象。

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

  • async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。

  • 抛出的错误对象会被catch方法回调函数接收到。

async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise  对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。

也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

async function fun(){     // return "hello wenbo";     throw new Error("ERROR"); } fun().then(v => console.log(v),reason=>console.log(reason));//Error: ERROR```

await命令

正常情况下,await命令后面是一个 Promise 对象,返回该对象的结果。如果不是 Promise 对象,就直接返回对应的值。

async function fun(){     return await "zhaoshun";     // 等价于 return "zhaoshun"; } fun().then(value=>console.log(value));//zhaoshun

另一种情况是,await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。

class Sleep{     constructor(timeout){         this.timeout = timeout;     }     then(resolve,reject){         const startTime = Date.now();         setTimeout(()=>resolve(Date.now() - startTime),this.timeout);               }  }  (     async ()=>{         const sleepTime = await new Sleep(1000);         console.log(sleepTime);//1012     } )()   // js里面没有休眠的语法,但是借助await命令可以让程序停顿的时间 const sleepFun = (interval) => {     return new Promise(resolve=>{         setTimeout(resolve,interval);     }) }  // 用法 const asyncFun = async ()=>{     for(let i = 1; i <= 5; i++){         console.log(i);         await sleepFun(1000);     } } asyncFun();

从上面可以看到,await命令后面是一个Sleep对象的实例。这个实例不是 Promise  对象,但是因为定义了then方法,await会将其视为Promise处理。

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

注意:上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。

  • 任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

  • 使用try/catch可以很好处理前面await中断,而后面不执行的情况。

示例:

const fun = async ()=>{     try {         await Promise.reject("ERROR");     } catch (error) {               }     return await Promise.resolve("success");  } fun().then(     value=>console.log(value),reason=>console.log(reason,"error")// ).catch(     error=>console.log(error)//ERROR );

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

const fun = async ()=>{     await Promise.reject("error").catch(e=>console.log(e));     return await Promise.resolve("success"); } fun().then(v=>console.log(v));//success

错误处理

第一点:如果await后面的异步操作出错,那么等同于async函数后面的promise对象被reject。

const fun = async()=>{     await new Promise((resolve,reject)=>{         throw new Error("error")     }) } fun().then(v=>console.log(v)).catch(e=>console.log(e));

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

const [fun1,fun2] = await Promise.all([getFun(),getFoo()]);  const fooPromise = getFoo(); const funPromise = getFun(); const fun1 = await fooPromise(); const fun2 = await funPromise();

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

async function dbFuc(db) {   let docs = [{}, {}, {}];    // 报错   docs.forEach(function (doc) {     await db.post(doc);   }); }

第四点:async 函数可以保留运行堆栈。

看完上述内容,你们掌握如何上手Promise和aysnc/await的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网html频道,感谢各位的阅读!

--结束END--

本文标题: 如何上手Promise和aysnc/await

本文链接: https://www.lsjlt.com/news/80810.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何上手Promise和aysnc/await
    如何上手Promise和aysnc/await,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。单线程,就是指一次只能完成一件任务,如果有多个任务...
    99+
    2024-04-02
  • Node.js Async/Await与Promise的异同:如何选择合适的异步编程方式?
    作为一门事件驱动的语言,Node.js 天生就支持异步编程。异步编程可以提高程序的并发能力,从而提高程序的性能。在 Node.js 中,有两种流行的异步编程方式:Async/Await 和 Promise。 Async/Await 是 ...
    99+
    2024-02-27
    Node.js、异步编程、Async/Await、Promise
  • Node.js中async和await关键字如何使用
    本篇内容主要讲解“Node.js中async和await关键字如何使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Node.js中async和await关键字如何使用”吧!async 和 aw...
    99+
    2023-07-02
  • 如何上手Mininet
    这篇文章给大家分享的是有关如何上手Mininet的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Mininet是由斯坦福大学基于Linux Container架构开发的一个进程虚拟化网络仿真工具,可以创建一个包含主...
    99+
    2023-06-27
  • 如何理解C# 5.0的新特性Async和Await
    今天就跟大家聊聊有关如何理解C# 5.0的新特性Async和Await,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、引言在之前的C#基础知识系列文章中只介绍了从C#1.0到C#4...
    99+
    2023-06-17
  • 如何快速上手Vue3
    这篇文章主要讲解了“如何快速上手Vue3”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何快速上手Vue3”吧!Vue2 与 Vue3 的对比对 TypeS...
    99+
    2024-04-02
  • 小白如何上手 SQL
    小白如何上手 SQL?SQL是一种用于管理和操作关系型数据库的标准编程语言。对于初学者来说,学习SQL可能会有些困难,但随着经验的积累,逐渐掌握这门语言是很有可能的。本文将提供一些简单且实用的方法,帮助小白快速上手SQL。 1. 了解基本...
    99+
    2024-01-24
    SQL数据库 虚拟主机知识
  • 如何快速上手MongoDB
    小编给大家分享一下如何快速上手MongoDB,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。...
    99+
    2023-06-27
  • 如何快速上手PostgreSQL
    本篇内容介绍了“如何快速上手PostgreSQL”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、安装首先,安装PostgreSQL客户端。...
    99+
    2023-06-27
  • 如何快速上手SaltStack
    这篇文章给大家分享的是有关如何快速上手SaltStack的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。SaltStack是一个服务器基础架构集中化管理平台,具备配置管理、远程执行、监控等功能,基于Python语言...
    99+
    2023-06-27
  • 如何快速上手GitHub
    这篇文章给大家分享的是有关如何快速上手GitHub的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。GitHub 是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 Git...
    99+
    2023-06-27
  • 如何快速上手coLinux
    这篇文章给大家分享的是有关如何快速上手coLinux的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。colinux位是一款可以帮助用户创建虚拟系统的工具,通过该软件,您可以在电脑上运行Linux系统,支持在win中...
    99+
    2023-06-27
  • 如何快速上手MMdnn
    这篇“如何快速上手MMdnn”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“如何快速上手MMdnn”文章吧。MMdnn 是微软...
    99+
    2023-06-27
  • 如何解析异步编程In .NET APM/EAP和async/await
    如何解析异步编程In .NET APM/EAP和async/await,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。概述在之前写的一篇关于async和await的前世今生的文章...
    99+
    2023-06-17
  • 如何快速上手SQL
    这篇文章主要为大家展示了“如何快速上手SQL”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何快速上手SQL”这篇文章吧。两张示例表为了方便练习SQL语法,特地...
    99+
    2024-04-02
  • 如何快速上手Rollup
    本篇内容主要讲解“如何快速上手Rollup”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何快速上手Rollup”吧!什么是rollup?系统的了解rollup...
    99+
    2024-04-02
  • 如何快速上手Picasso
    这篇文章将为大家详细讲解有关如何快速上手Picasso,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。Picasso是Square公司出品的一款非常优秀的开源图片加载库,是目前Android开发中超级流行的...
    99+
    2023-06-27
  • 如何用ES7中的Async和Await进行异步编程
    这篇文章给大家介绍如何用ES7中的Async和Await进行异步编程,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Async/Await基本规则async 表示这是一个async函数,...
    99+
    2024-04-02
  • Promise和Generato中如何用同步方法写异步JavaScript
    这篇文章给大家介绍Promise和Generato中如何用同步方法写异步JavaScript,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。最近在写一个自己的网站的时候(可以观摩一下~C...
    99+
    2024-04-02
  • PHP和Bash:如何在LeetCode上成为编程高手?
    LeetCode是一个非常受欢迎的在线编程练习平台,它提供了大量的算法题目,可以帮助程序员锻炼编程能力。在LeetCode上,使用PHP和Bash这两种编程语言可以帮助你更快地完成编程任务。在本文中,我们将介绍如何使用PHP和Bash在L...
    99+
    2023-08-17
    bash leetcode 关键字
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作