async和await

js中有两种实现异步的方式

传统的回调函数

比如使用setTimeout让一个函数在指定的时间后执行 ,这个函数本身会立刻返回,程序紧接着执行之后的代码,但是自己传入的回调函数则会等到预定的时间才会执行

setTimeout(()=>{
 console.log("兄弟你好");
},3000);
console.log("你会立刻看到我");

 Javascript从设计之初就是一个单线程的编程语言,即便看上去回调函数和主程序在并发执行,但是它们都运行在同一个主线程当中

单线程的优点:无需考虑线程同步或者是资源竞争的问题,从源头上避免了线程之间的频繁切换,降低了线程自身的开销

 缺点:

回调函数虽然简单好理解,但是有一个明显的缺点,如果需要依次执行多个异步操作,程序可能会写成如下这样,当地一个任务执行完成之后,在回调函数中再去执行第二个任务,之后是第三个,第四个,整个程序会一层一层的嵌套下去,可读性变差,也就是函数的回调地狱 。

setTimeout(){
 console.log("等三秒后");
 setTimeout(){
  console.log("再等三秒后");
   setTimeout(()=>{
     console.log("又等三秒");
       //...
     },3000);
 },3000);
},3000);

因此,Promise应运而生

javaScript使用promise当中的API ,fetch(’http://...‘)就是一个很好的例子,用来发起一个请求来获取服务器数据,可以用它来动态更新页面的内容,也就是平时说的Ajax技术

我们随后可以调用它的then方法并传递一个回调函数,如果请求在未来成功完成,那么回调函数会被调起,请求的结果也会用参数的形式传递进来

fetch("https://jsonplaceholder.typicode.com/post/1")
.then(()=>{})

fetch("https://jsonplaceholder.typicode.com/post/1")
.then((response)=>{
  //......
})

Promise的优点:可以用一种链式结构将多个异步操作串联起来

fetch("https://jsonplaceholder.typicode.com/post/1")
.then((response)=>response.json());
这里面的response.json()方法也会返回一个Promise,代表
未来的某个时刻将返回的数据转换成JSON格式,如果想要等到他完成
之后再执行其它操作,可以在后面追加一个.then
fetch("https://jsonplaceholder.typicode.com/post/1")
.then((response)=>response.json());
.then((json)=>console.log(json));

Promise的链式调用,避免了代码的层层嵌套

对比:

代码是向下方增长而不是向右,因此可读性会提升很多

//回调函数
setTimeout(){
 console.log("等三秒后");
 setTimeout(){
  console.log("再等三秒后");
   setTimeout(()=>{
     console.log("又等三秒");
       //...
     },3000);
 },3000);
},3000);

//链式调用:
fetch("https://...")
.then(...)
.then(...)
.then(...)
.then(...)
.then(...);

捕捉错误:

同时Promise提供finally方法,在Promise链结束之后调用,无论失败与否,可以在这里做一些清理工作

fetch("https://jsonplaceholder.typicode.com/post/1")
.then((response)=>response.json());
.then({(json)=>console.log(json);})
.catch((error)=>{
console.log(error);
})
.finally(())=>{
//执行清理操作等等
//清理加载动画	 :
stopLoadingAnimation();
})

async和await是基于Promise之上的一个语法糖,可以让异步操作更加的简洁明了

第一:使用async关键字将函数标记为异步函数,也就是返回值是Promise对象的函数。

比如之前用到的fetch()就是一个异步函数,在异步函数中可以调用其它的异步函数,不过,不再需要使用then,而是一个更加简洁的await语法

await会等待Promise完成之后直接返回最终的结果。所以

async function f(){
await fetch("http://...");

const response=await fetch("http://...");
//这里的response已经是服务器返回的响应数据了
const json=await response.json();
console.log(json);

}
f();//注意:这个函数返回值永远是Promise对象

await虽然看上去会暂停函数的执行,但是在等待的过程中,javaScript同时可以处理其它的任务,比如更新界面,运行其他的程序代码等等,这是因为await底层是基于Promise和事件循环机制实现的

async funtion f(){
 const a=await fetch("http://.../post/1");
 const b=await fetch("http://.../post/2");
 //...
 Promise.all([promiseA,promiseB]);
 //...
 const [a,b]=await  Promise.all([promiseA,promiseB]);
}

第二个:如果我们需要在循环中执行异步操作,不能直接调用forEach或者是map这一类方法的。

尽管在回调函数中写了await,但是里面的forEach会立刻返回,并不会暂停等到所有的异步操作都执行完毕,

如果希望等待循环中的异步操作都一一完成之后才继续执行,还是应该使用传统的for循环

async function f(){
 [1,2,3].forEach(async(i)=>{
  await someAsyncOperation();
 });
console.log("done");

//使用for循环
for(let i of [1,2,3]){
await someAsyncOperation();
}
console.log("done");
}
f();

更进一步,如果想要循环中的所有操作都并发执行,更炫酷的写法是使用for await,这里的for循环依然会等到所有的异步操作都完成之后才继续向后执行

async funtion f(){
const promise=[
 someAsyncOperation(),
 someAsyncOperation(),
 someAsyncOperation(),
];
for await(let result of promises){
//...
}
console.log("done")
}
f();

第三个:我们不能在全局或者普通函数中直接使用await关键字,await只能被用在异步函数(async funtion)中

await someAsyncOperation();  //❌

如果想要在最外层中使用await,那么需要先定义一个异步函数,然后在函数体当中使用它

async funtion f(){
await someAsyncOperation();
}
f();
//或者是更加简洁的写法
(async()=>{
await someAsyncOperation();
})();

使用async和await之后,可以写出更加清晰地异步代码,有了它们之后,我们几乎不再需要使用底层的Promsie对象,包括调用它们的then(),catch()函数等等。

拓展:

一:.Promise.all() 方法的作用是什么

Promise.all()方法同时处理多个Promise。它以一个Promise数组作为输入,并且返回一个新的Promise。这个新的Promise在输入数组中的所有Promise都已经解决的时候才会解决。已经解析的Promise的值在.then()块中作为数组按照输入Promise的顺序可用。

2.如何处理 Promise 中的错误?

可以使用  try  { }  catch(err) {  } finally 块来处理 Promise 链中的错误,

3.同步操作和异步操作之间有什么区别?

同步操作会阻止程序的执行,直到操作完成。它们以顺序方式一个接一个地执行。而异步操作不会阻止程序的执行,它们允许程序在等待操作完成时继续执行其他任务。异步操作通常用于可能需要较长时间才能完成的任务,例如网络请求或文件操作,以避免阻塞主执行线程

4. JavaScript 中回调函数和 Promise 之间有什么区别?什么时候更喜欢使用 Promise而不是回调?另外,回调地狱是什么,如何缓解它?

回调是 JavaScript 中处理异步操作的传统方式,它们是作为参数传递给其他函数的函数,并在异步操作完成时调用。Promise 是表示异步操作最终完成(或失败)的对象。与回调相比,Promise 提供了更结构化和可读的代码。通常,当处理复杂的异步操作、错误处理和代码可读性时,更倾向于使用 Promise 而不是回调。回调地狱是指代码结构变得嵌套层次很深,每个回调都作为另一个回调的参数传递。这种嵌套会很快变得复杂,使代码难以理解,导致问题,如代码重复、错误处理问题以及难以维护和调试的困难。为了缓解回调地狱,可以使用几种方法,例如使用命名函数、使用控制流库(如 asyncjs 或 Promises)或使用现代 JavaScript 特性如 async/await。这些方法有助于扁平化代码结构,使其更可读和可维护,避免过多的回调嵌套。

5.用Promise解释.catch() 方法的用途:

它与.then()方法有什么不同?promises 中的.catch()方法用于错误处理。它允许您指定一个回调函数,当promise 在执行过程中被拒绝或遇到错误时将调用该回调函数。.then()用于处理完成的 promises 和成功的结果。

6.Promise.resolve和Promise.reject()的区别是什么?

Promise.resolve:它返回用给定值解析的 promises。如果提供的值已经是 Promise,则按原样返回。如果该值不是 Promise, Promise.Resolve()将创建一个新的Promise,该新 Promise 将立即使用提供的值进行解析。Promise.reject0):它返回因给定原因或错误而被拒绝的 promises。所提供的原因或错误被视为拒绝 promises 的原因。


儿童节快乐,希望,世界和平。

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值