“约见”面试官系列之常见面试题第三十二篇之async和await(建议收藏)

一、async和await

  1. async和await的概念

1)async 函数是 Generator 函数的语法糖,使用 关键字 async 来表示,在函数内部使用 await 来表示异步
2)ES7 提出的async 函数,终于让 JavaScript 对于异步操作有了终极解决方案
3)async 作为一个关键字放到函数的前面,用于表示函数是一个异步函数,该函数的执行不会阻塞后面代码的执行
4)await是等待,只能放到async函数里面,在后面放一个返回promise对象的表达式
5)async和await是为了解决大量复杂不易读的Promise异步的问题

  1. async函数的改进

1)内置执行器,Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
2)更好的语义,async 和 await 相较于 * 和 yield 更加语义化
3)更广的适用性,co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise对象,而 async 函数的 await 命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)
4)返回值是 Promise,async 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用
5)async方式,流程清晰,直观、语义明显,操作异步流程就如同操作同步流程, async 函数自带执行器,执行的时候无需手动加载。对于Promise的方式,如果处理流程复杂,整段代码将会充满then,不然很好的表示流程。对于Generator 方式,函数的执行需要依靠执行器,每次都需要通过 g.next() 的方式去执行

  1. async和await的实例

1)async 作为一个关键字放到函数的前面,用于表示函数是一个异步函数,该函数的执行不会阻塞后面代码的执行
实例代码:

	async function timeout(){
        return "hello word";
    }

   timeout();
    //  Promise   __proto__: Promise  [[PromiseStatus]]: "resolved"   [[PromiseValue]]: "hello word"
    //async返回的是 promise 对象
    console.log(timeout());
    // 我在后面,但是我是先执行的
   console.log("我在后面,但是我是先执行的");

2)async声明的函数的返回本质上是一个Promise,async函数内部会返回一个Promise对象,then方法回调函数的参数
实例代码:

    //  hello world11
    async function f1(){
        return "hello world11";
    };
    f1().then((v)=>console.log(v));

3)await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,用await声明的Promise异步返回,必须“等待”到有返回值的时候,代码才继续执行下去
实例代码:

	/*  hello1
    我是hello2
    输出 hello1*/

    const test = async()=>{
        let message = "hello1";
        let result = await message;
        console.log(result);
        console.log("我是hello2");
        return result;
    };
    test().then(result =>{
        console.log("输出",result);
    });

4)async 函数内部的实现原理是resolved,如果函数内部抛出错误, 则会导致返回的 Promise 对象状态变为 reject 状态,promise 对象有一个catch 方法进行捕获,被 catch 方法回调函数接收到
实例代码:

	async function timeout2(flag){
        if(flag){
            return "hello world";
        }else{
            throw "failed";
        }
    }

    // 如果函数内部抛出错误, promise 对象有一个catch 方法进行捕获
    timeout2(false).catch(err => {
        console.log(err);
    });

    // hello word
    // 调用Promise.resolve() 返回promise 对象
    console.log(timeout2(true));
    // Uncaught (in promise) failed
    // 调用Promise.reject() 返回promise 对象
    console.log(timeout2(false));

5)async必须声明的是一个function,await就必须是在这个async声明的函数内部使用,必须是直系,作用域链不能隔代,在后面放一个返回promise对象的表达式
实例代码:

 // 需求:2s后让数值乘以2
    function doubleAfter(num){
        return new Promise((resolve,reject)=>{
            setTimeout(()=>{
                resolve(2*num)
            },2000);
        });
    }

    //  60
    // 等待2s后,promis开始resolve,返回值
    async function test(){
        let result = await doubleAfter(30);
        console.log(result);
    }
    test();

    // 240
    //等待6s后,promis开始resolve,返回值
    async function test2(){
        let first = await doubleAfter(30);
        let second = await doubleAfter(40);
        let third = await doubleAfter(50);
        console.log(first+second+third);
    }
    test2();

6)函数的错误处理,当 async 函数中只要一个 await 出现 reject 状态,则后面的 await 都不会被执行,可以添加 try/catch
实例代码:

let a;
    async function f5(){
        await Promise.reject("error");
        a = await 1;
    }
    //f5().then(v=>console.log(a));

    //  try/catch 解决
    // Uncaught (in promise) ReferenceError: error is not defined
    at f6 (test13.html:124)
    let a3;
    async function f6(){
        try{
             await Promise.reject("error");
        }catch{
            console.log(error);
        }
        a3 = await 123;
        return a3;
    }

    f6().then((v)=>console.log(a3));
  1. async/await的实战

1)需求分析:有两个延时函数,先延时1秒,在延迟2秒,再延时1秒,最后输出“完成”
2)实例代码:

//  延时函数
const setDelay = (millisecond) => {
  return new Promise((resolve, reject)=>{
      if (typeof millisecond != 'number') reject(new Error('参数必须是number类型'));
      setTimeout(()=> {
        resolve(`我延迟了${millisecond}毫秒后输出的`)
      }, millisecond)
  })
}
const setDelaySecond = (seconds) => {
  return new Promise((resolve, reject)=>{
      if (typeof seconds != 'number' || seconds > 10) reject(new Error('参数必须是number类型,并且小于等于10'));
      setTimeout(()=> {
        resolve(`我延迟了${seconds}秒后输出的,注意单位是秒`)
      }, seconds * 1000)
  })
}

 /*  我延迟了${millisecond}毫秒后输出的
    我延迟了${seconds}秒后输出的,注意单位是秒
    我延迟了${millisecond}毫秒后输出的
    完成*/
(async ()=>{
  const result = await setDelay(1000);
  console.log(result);
  console.log(await setDelaySecond(2));
  console.log(await setDelay(1000));
  console.log('完成了');
})()

二、async和await的面试题总结

  1. 谈谈对 async/await 的理解,async/await 的实现原理是什么?
    1)async/await 就是 Generator 的语法糖,使得异步操作变得更加方便
    2)async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成await
    3)async 是 Generator 的语法糖,这个糖体现在这几个方面:
  • async函数内置执行器,函数调用之后,会自动执行,输出最后结果,而Generator需要调用next或者配合co模块使用
  • 更好的语义,async和await,比起星号和yield,语义更清楚了,async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
  • 更广的适用性,co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async 函数的 await 命令后面,可以是 Promise 对象和原始类型的值
  • 返回值是Promise,async函数的返回值是 Promise 对象,Generator的返回值是 Iterator,Promise 对象使用起来更加方便
    4)async/await 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里
    5)实例代码分析:
function my_co(it) {
    return new Promise((resolve, reject) => {
        function next(data) {
            try {
                var { value, done } = it.next(data);
            }catch(e){
                return reject(e);
            }
            if (!done) { 
                //done为true,表示迭代完成
                //value 不一定是 Promise,可能是一个普通值。使用 Promise.resolve 进行包装。
                Promise.resolve(value).then(val => {
                    next(val);
                }, reject);
            } else {
                resolve(value);
            }
        }
        next(); //执行一次next
    });
}
function* test() {
    yield new Promise((resolve, reject) => {
        setTimeout(resolve, 100);
    });
    yield new Promise((resolve, reject) => {
        // throw Error(1);
        resolve(10)
    });
    yield 10;
    return 1000;
}

my_co(test()).then(data => {
    console.log(data); //输出1000
}).catch((err) => {
    console.log('err: ', err);
});
  1. 使用 async/await 需要注意什么?
    1)await 命令后面的Promise对象,运行结果可能是 rejected,此时等同于 async 函数返回的 Promise 对象被reject。因此需要加上错误处理,可以给每个 await 后的 Promise 增加 catch 方法;也可以将 await 的代码放在 try…catch 中
    2)多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发
    实例代码:
//下面两种写法都可以同时触发
//法一
async function f1() {
    await Promise.all([
        new Promise((resolve) => {
            setTimeout(resolve, 600);
        }),
        new Promise((resolve) => {
            setTimeout(resolve, 600);
        })
    ])
}
//法二
async function f2() {
    let fn1 = new Promise((resolve) => {
            setTimeout(resolve, 800);
        });
    
    let fn2 = new Promise((resolve) => {
            setTimeout(resolve, 800);
        })
    await fn1;
    await fn2;
}

3)await命令只能用在async函数之中,如果用在普通函数,会报错
4)async 函数可以保留运行堆栈
实例代码:

/
* 函数a内部运行了一个异步任务b()。当b()运行的时候,函数a()不会中断,而是继续执行。
* 等到b()运行结束,可能a()早就* 运行结束了,b()所在的上下文环境已经消失了。
* 如果b()或c()报错,错误堆栈将不包括a()。
*/
function b() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 200)
    });
}
function c() {
    throw Error(10);
}
const a = () => {
    b().then(() => c());
};
a();
/**
* 改成async函数
*/
const m = async () => {
    await b();
    c();
};
m();

 

异步编程是现代软件开发中非常重要的一部分,而在JavaScript中,async和await是用来处理异步操作的两个关键字。下面是一些关于async和await面试题: 1. async和await是什么?它们的作用是什么? - async是用来声明一个函数是异步的,它会返回一个Promise对象。 - await用于等待一个Promise对象的解析结果,并且只能在async函数内部使用。它可以暂停函数的执行,直到Promise对象的状态变为resolved(已解决)。 2. async函数返回什么? - async函数始终返回一个Promise对象。这个Promise对象的结果取决于函数中的return语句。 3. 在什么情况下使用async和await? - 当需要处理异步操作时,可以使用async和await来编写更清晰、更易读的异步代码。 - 通常情况下,我们会将回调函数、Promise链式调用或者Generator函数转换为使用async和await的代码。 4. async函数中可以有多个await吗? - 是的,async函数中可以有多个await语句。每个await语句会暂停函数的执行,直到对应的Promise对象解析为已解决状态。可以按需使用多个await语句来处理多个异步操作。 5. async函数中如何处理错误? - 可以使用try-catch语句块来捕获和处理错误。在使用await等待一个Promise对象时,如果该Promise对象的状态变为rejected(已拒绝),则会抛出一个异常,可以通过try-catch来捕获并处理该异常。 这些面试题可以帮助你了解async和await的基本概念和用法。当然,还有更多的问题可以进一步探讨和学习。
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值