异步处理-async和await

155 阅读1分钟

概述

async和await是ES2016新增的两个关键字,它们借鉴了ES2015中生成器在实际开发中的应用,目的是简化Promise api的使用,并非是替代 Promise。只是一个语法糖。

async

目的是为了简化在函数的返回值中对Promise的创建。

async用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始位置,被修饰函数的返回结果一定是Promise对象。

函数字面量: function test(){} 函数表达式:匿名函数或箭头函数

async function test(){
    console.log('1');
    return 2;
}

// 等效于
function test() {
    return new Promise((resolve, reject) => {
        console.log('1');
        resolve(2);
    })
}
// 如何返回reject
async function test(){
    console.log('1');
    throw 3;
    return 2;
}

await

await关键字必须出现在async函数中!!!

await用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。

async function test1() {
    console.log(1);
    return 2;
}

async function test2() {
    const result = await test1();
    console.log(result);
    return undefined;

    // 等效于
    // return new Promise((resolve, reject) => {
    //     test1().then((data) => {
    //         const result = data;
    //         console.log(result);
    //         resolve();
    //     });
    // });
}
test2();

举例:获取李华所在班级的老师的信息

思路:

1.获取李华的班级id

2.根据班级id获取李华所在班级的老师的id

3.根据老师id查询老师的信息

async function getTeacher() {
    // 获取所有的学生
    const stus = await ajax({
        url: 'students.json'
    })
    // 等效于
    // ajax({
    //     url: ''
    // }).then(resp => {
    //     const stus = resp;
    // })

    // 获取李华的班级id
    let cid;  
    for (let i = 0; i < stus.length; i++) {
        if (stus[i].name === "李华") {
            cid = stus[i].classId;
        }
    }

    // 获取所有的班级
    const cls = ajax({
        url:'classes.json'
    })
    // 获取老师的id
    let tid;
    for (let i = 0; i < cls.length; i++) {
        if (cls[i].id === cid) {
            tid = cls[i].teacherId;
        }
    }

    // 获取所有老师
    const ts = await ajax({
        url: "./data/teachers.json"
    })
    // 获取老师的信息
    for (let i = 0; i < ts.length; i++) {
        const element = ts[i];
        if (element.id === tid) {
            console.log(element);
        }
    }
    }

    getTeacher();

分析:

  1. 在async函数里调用ajax
  2. async函数返回Promise
  3. 只要返回Promise就可以等待(await)
  4. 等待resolve之后,就可以获取状态数据
  5. async里的代码全是放到then里面的

举例:

垦哥心中有三个女神; 有一天,垦哥决定向第一个女神表白,如果女神拒绝,则向第二个女神表白,直到所有的女神都拒绝,或有一个女神同意为止。

用代码模拟上面的场景。

(async () => {
    const gods = ["女神1", "女神2", "女神3", "女神4", "女神5"];
    for (let i = 0; i < gods.length; i++) {
        const g = gods[i];
        // 当前循环等待的Promise没有resolve,下一次循环不运行
        const result = await biaobai(g);
        if(result){
            console.log(`${g}同意了,不用再表白了!!!`);
            break;
        } else {
            console.log(`${g}没有同意`)
        }
    }
})()

function biaobai(god) {
    return new Promise(resolve => {
        console.log(`垦哥向${god}发出了表白短信`);
        setTimeout(() => {
            if (Math.random() < 0.3) {
                //女神同意拉
                resolve(true)
            } else {
                //resolve
                resolve(false);
            }
        }, 500);
    })
}

特殊场景

场景一:

如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行。

async function test() {
    const result = await 1;
    console.log(result)
}

// 等效于
function test() {
    return new Promise((resolve, reject) => {
        Promise.resolve(1).then(data => {
            const result = data;
            resolve();
        })
    })
}

test();
console.log(123);

场景二:

await 错误的情况

async function getPromise() {
    if (Math.random() < 0.5) {
        return 1;
    } else {
        throw 2;
    }
}
async function test() {
    // 等效于
    // getPromise().then(data => {
    //     const result = await getPromise();
    //     console.log('正常状态', result)
    // }, error => {
    //     console.log('错误状态', error)
    // })
    try {
        const result = await getPromise();
        console.log('正常状态', result)
    } catch (error) {
        console.log('错误状态', error)
    }
}
test();

改造计时器

function delay(duration) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve();
        }, duration)
    })
}

async function biaobai(god){
    console.log(`垦哥向${god}发出了表白短信`);
    await delay(500);
    return Math.random() < 0.3
}