18888628835/Blog

async 和 await

18888628835 opened this issue · 0 comments

async和await

简单来说,它们是基于promises的语法糖,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是老式同步代码,因此它们非常值得学习。

async关键字

async function hello() { return "Hello" };
hello();
//Promise {<fulfilled>: "Hello"}

上面的代码增加async关键字后,这个函数会返回promise。这是异步的基础,可以说,现在的异步JS就是使用promise。

箭头函数写法

let hello=async ()=>{return 'hello'}

现在我们可以使用.then方法啦

hello().then((mes)=>{console.log(mes)})

await关键字

await只在异步函数里面才起作用,它的主动作用是会暂停代码在该行上,直到promise完成,然后返回结果值,await后面应该放promise对象

我们可以造一个实际的例子

const p=new Promise((resolve,reject)=>{
  setTimeout(resolve,3000,'doing')
})
const r=new Promise((resolve,reject)=>{
  setTimeout(resolve,0,p)
})
const o=r.then((mes)=>{
  return mes+'=>done'
})
o.then((mes)=>{console.log(mes)}).catch((error)=>{console.log(error)})
//doing => done

上面代码中,r会拿到p的结果,然后链式调用下去。

我们可以使用async+await进行封装

    function promise(ms, mes) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms, mes);
      });
    }
    async function fn() {
      const p = await promise(3000, "doing");
      console.log(p); // doing
      const r = await promise(0, p);
      console.log(r); //doing
      const o = await (r + "=>done");
      console.log(o); //doing =>done
    }
    fn();

可以看到上面的异步代码就跟同步代码的写法一样。

asyncawait要同时使用才会有异步的效果,单单使用async依然是同步代码,只是返回promise对象

错误处理

在使用async/await关键字的时候,错误处理是关键,一般我们会这么写来捕捉错误

function ajax(){
   return Promise.reject(1)
}
async function fn(){
   try{
      const result=await ajax()
      console.log(result)
   }catch(error){
      console.log(error)
   }
}
fn() 

下面我们可以使用更好的方法

function ajax(){
   return Promise.reject(1)
}
function ErrorHandler(error){
    throw Error(error)
}
async function fn(){
   const result=await ajax().then(null,(error)=>{ErrorHandler(error)})
   console.log('result',result)
}
fn()

这里要注意的就是ErrorHandler时不要用return,以免把结果返回给result,使用throw Error就可以抛出一个错误。那么后续的代码就不会执行了

await的传染性

function async2(){
console.log('async2')
}
async function fn(){
   console.log('fn')
   await async2() //同步的
   console.log('我是异步?')
}
fn()
console.log('end')
//fn
//async2
//end
//我是异步?

最后的console.log('我是什么步?')是后于await关键字的,说明它是异步的,如果我们想执行同步代码,最好都放在await的上面,因为有时候await会带给我们疑惑,会误以为没有写await关键字的代码是同步的。

也许你会怀疑是否第一行log也是异步的,下面这个代码可以告诉你答案,并非写了async关键字就代表这是异步函数。

let a=0
async function fn(){
   console.log(a)
   await Promise.resolve(333).then((r)=>{console.log(r)})
   console.log('我是什么步?')
}
fn()
console.log(++a)
//结果
/*
0
1
333
"我是什么步?"
*/

串行和并行

await天生是串行的,所谓串行,就是按照顺序执行。

function async2(delay){
  return new Promise((resolve)=>{
    setTimeout(()=>{
      console.log('执行')
      resolve()
    },delay)
  })
}                  
async function fn(){
  await async2(5000)
  await async2(2000)
  await async2(1000)
}
fn()

由于async跟setTimeout同时用没有效果,所以我使用上面的代码做实验,log台五秒钟后会分别打印,这说明默认就是按照顺序执行await的

如果想要并行,就可以使用Promise.all或者forEach方法

function fn(){
  await Promise.all([async2(5000),async2(2000),async2(1000)])
}
function async2(delay){
  return new Promise((resolve)=>{
    setTimeout(()=>{
      console.log('执行')
      resolve()
    },delay)
  })
}
                     
function fn3(ms){
  return function fn(){
    async2(ms)
  }
}

[fn3(5000),fn3(2000),fn3(1000)].forEach(async (v)=>{
  await v()
})

与fetch相结合

fetch就是使用promise版本的XMLHttpRequest

fetch('products.json').then(function(response) {
  return response.json();
}).then(function(json) {
  console.log(json)
}).catch(function(err) {
  console.log('Fetch problem: ' + err.message);
});

上面的代码的意思是通过fetch申请一个json数据,然后得到数据后将其json化,再打印出来。

转化成async和await方法

const promise=()=>{
  try{
     const j=await fetch('products.json')
     const result=await j.json()
     console.log(result)
  }catch(error){
     console.log(error)
  }
}
promise()

在非 async 函数中调用 async 函数

我们有一个名为 f 的“普通”函数。你会怎样调用 async 函数 wait() 并在 f 中使用其结果?

async function wait() {
  await new Promise(resolve => setTimeout(resolve, 1000));

  return 10;
}

function f() {
  // ……这里你应该怎么写?
  // 我们需要调用 async wait() 并等待以拿到结果 10
  // 记住,我们不能使用 "await"
}

其实解决方法很简单,由于 await 会返回一个 promise,所以直接在后面写 then 方法就可以拿到结果

function f() {
  wait().then(result => alert(result));
}