欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES7 async/await

程序员文章站 2022-06-15 14:11:07
...

ES7引入的async/await在JavaScript异步编程中是一个很好的改进,它提供了使用同步样式的代码异步访问resources资源的方式,而且不会阻塞线程。

  • async/await用于编写JavaScript异步程序
  • async/await代码书写方式和同步编程相似,代码简洁易读。
  • async/await基于Promise
  • async/await中可使用try-catch捕获异常

async

  • async函数

async函数返回的是一个Promise对象,async函数包括函数语句、函数表达式、Lambda表达式。

async函数会返回一个Promise对象,如果在async函数中return一个直接量,async会将这个直接量通过Promise.resolve()封装成Promise对象。

async function fn(){
    return 1;
}
console.log(fn());//Promise {<resolved>: 1}
ES7 async/await
image.png

async函数调用时会返回Promise对象,Promise对象拥有status和value。如果async函数拥有返回值,当调用async函数时,函数内部会调用Promise.resolve()方法将其转化为一个Promise对象并作为返回。

如果async函数没有返回值,则会返回Promise.resolve(undefined)。

async function fn(){
}
console.log(fn());//Promise {<resolved>: undefined}
ES7 async/await
image.png

async函数调用时函数内部如果抛出错误则出现错误

async function fn(){
    throw new Error("rejected");
}
console.log(fn());
ES7 async/await
image.png
  • async关键放在函数前,async关键字后意味着函数将返回一个Promise。

在函数前添加async关键字后意味着函数将返回一个Promise,JavaScript编译器会自动函数返回值转换为一个Promise。

async function fn(){
  return 1;
}
fn().then(alert);//1
function fn(){
    return Promise.resolve(1);
}
fn().then(alert);//1
fn().then(console.log);//1

如果要获取async函数的执行结果,需要调用Promise对象的then或catch来为其注册回调函数。

async function fn(){
    return 1;
}
fn().then(result=>console.log(result));//1

如果async函数执行完毕返回的Promise对象没有注册回调函数,比如函数内部做了一次for循环此时函数的调用就只是执行了函数体,这和普通的函数并没有区别。唯一的不同之处在于函数体执行完毕后返回的是一个Promise对象。

async function fn(){
    for(let i=0; i<10; i++){
        console.log("async fn:", i);
    }
}
console.log(fn());
ES7 async/await
image.png

async函数的执行会返回一个Promise对象,并将内部的值进行promise封装,如果Promise对象通过then或catch方法注册了回调函数,async函数执行完毕后,注册的回调函数会放入到异步队列中等待执行。如果只是async函数则和promise一样,但有了await之后就不一样的,await关键字只能放到async函数中,await等待的是什么呢?

await

await等待的是一个表达式,这个表达式的计算结果是Promise对象或其他值,换句话说await可以等待任意表达式的结果。如果await等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。如果await等到的是一个Promise对象,await就会阻塞后续代码,等到Promise对象resolve,然后得到resolve的值作为await表达式的运算结果。

这也就是await必须用在async函数中的原因,async函数调用不会造成阻塞,async函数内部所有的阻塞都被封装在一个Promise对象中异步执行。

let result = await promise;
  • await关键字的含义是让JavaScript编译器等待Promise并返回结果

await的意思是让JavaScript编译器等待Promise结束,然后再输出结果。这里并不会占用CPU资源,因为JavaScript引擎可以同时执行其它任务或脚本或处理事件。

async function fn(){
    
    let promise = new Promise((resolve, reject)=>{
        setTimeout(()=>resolve("done"), 1000);
    });
    let result = await promise;//wait till the promise resolve
    console.log(result);//done
}
fn();

fn函数执行时会在let result = await promise位置暂停,直到Promise返回结果,因此代码会在1秒后在控制台输出done。

  • await只能在async中运行

await不能单独使用,必须在async函数的作用域下使用,否则将会报异常Error:await is only valid in async function

function fn(){
    let promise = new Promise((resolve, reject)=>{
        setTimeout(()=>resolve("done"), 1000);
    });
    let result = await promise;//Uncaught SyntaxError: await is only valid in async function
    console.log(result);
}
fn();

例如:摇色子等待3秒后得到最终的结果

function fn(){
    let val = parseInt(Math.random() * 6 + 1);
    let promise = new Promise((resolve, reject)=>{
        setTimeout(()=>resolve(val), 3000);
    });
    return promise;
}
async function main(){
    let val = await fn();
    console.log(val);
}
main();

例如:摇色子猜大猜小,成功与失败的处理。

function fn(type){
    return new Promise((resolve, reject)=>{
        let val = parseInt(Math.random() * 6 + 1);
        if(val > 3){
            if(type === "big"){
                resolve(val);
            }else{
                reject(val);
            }
        }else{
            if(type === "big"){
                reject(val);
            }else{
                resolve(val);
            }
        }
        setTimeout(()=>resolve(val), 3000);
    });
}
async function main(){
    try{
        let val = await fn("big");
        console.log("win:", val);
    }catch(error){
        console.error("fail:", error);
    }
}
main();

例如:有两个色子都买大,猜成功则返回两个的值,猜失败则返回第一个失败的结果。

function fn(type){
    return new Promise((resolve, reject)=>{
        let val = parseInt(Math.random() * 6 + 1);
        if(val > 3){
            if(type === "big"){
                resolve(val);
            }else{
                reject(val);
            }
        }else{
            if(type === "big"){
                reject(val);
            }else{
                resolve(val);
            }
        }
        setTimeout(()=>resolve(val), 3000);
    });
}
Promise.all([fn("big"), fn("big")]).then(
    (val)=>console.log("resolve:", val), 
    (val)=>console.log("reject:",val)
);

例如:话费充值,当用户输入电话号码后先查找电话号码所在的省市,在根据省市找到可能的重置面额。

ES7 async/await
charge

创建项目安装依赖

$ mkdir charge && cd charge
$ npm init
$ npm i --save express
$ npm i --save body-parse
$ npm i -g supervisor

创建服务器

$ vim index.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();

app.use(express.static("public"));
app.use(bodyParser.json());

app.post("/location", (req, res, next)=>{
    const phone = req.body.phone;
    setTimeout(()=>{
        res.json({error:false, code:200, message:"success", result:{province:"湖南", city:"长沙"}});
    }, 1000);
});
app.post("/facelist", (req, res, next)=>{
    const province = req.body.province;
    const city = req.body.city;
    setTimeout(()=>{
        res.json({error:false, code:200, message:"success", result:["20元", "30元", "50元"]});
    }, 1000);
});

app.listen(3000, ()=>console.log("server start"));

创建视图界面

$ vim /public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
    <title>Document</title>
</head>
<body>
    <div id="app" class="container">
        <h3 class="text-center">手机话费充值</h3>
        <hr>
        <div class="form-group">
            <div class="input-group">
                <div class="input-group-prepend">
                    <label class="input-group-text">电话号码</label>
                </div>
                <input type="text" class="form-control" v-model="phone">
                <span class="input-group-append">
                    <button class="btn btn-primary" @click="getItems">确定</button>
                </span>
            </div>
        </div>
        <hr>
        <div id="result">
            <div class="form-group" v-show="show">
                <div class="input-group">
                    <div class="input-group-prepend">
                        <div class="input-group-text">所属地区</div>
                    </div>
                    <input type="text" class="form-control" v-model="province"/>
                    <input type="text" class="form-control" v-model="city"/>
                </div>
            </div>
            <div class="form-group" v-for="item in items" :key="item">
                <div class="input-group">
                    <div class="input-group-prepend">
                        <div class="input-group-text">
                            <input type="radio">
                        </div>
                    </div>
                    <input type="text" class="form-control" :value="item">
                </div>
            </div>
        </div>
    </div>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.min.js"></script>
<script>
    const el = "#app";

    let data = {};
    data.phone = "";
    data.province = "";
    data.show = false;
    data.city = "";
    data.items = [];

    const vm = new Vue({
        el:el,
        data:data,
        methods:{
            getItems(){
                console.log(this.phone);
                this.getLocation(this.phone).then(res=>{
                    if(res.status === 200 && res.data.error===false){
                        const province = res.data.result.province;
                        const city = res.data.result.city;

                        this.show = true;
                        this.province = province;
                        this.city = city;

                        this.getFacelist(province, city).then(res=>{
                            if(res.status===200 && res.data.error===false){
                                this.items = res.data.result;
                            }
                        }).catch(err=>console.error(err));
                    }else{
                        console.error(res.data.message);
                    }
                }).catch(err=>console.error(err));
            },
            getLocation(phone){
                return axios.post("/location", {phone});
            },
            getFacelist(province, city){
                return axios.post("/facelist", {province, city});
            }
        }
    });
</script>
</body>
</html>

访问页面 http://127.0.0.1:3000