ES7 async/await
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}
async函数调用时会返回Promise对象,Promise对象拥有status和value。如果async函数拥有返回值,当调用async函数时,函数内部会调用Promise.resolve()方法将其转化为一个Promise对象并作为返回。
如果async函数没有返回值,则会返回Promise.resolve(undefined)。
async function fn(){
}
console.log(fn());//Promise {<resolved>: undefined}
async函数调用时函数内部如果抛出错误则出现错误
async function fn(){
throw new Error("rejected");
}
console.log(fn());
- 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());
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)
);
例如:话费充值,当用户输入电话号码后先查找电话号码所在的省市,在根据省市找到可能的重置面额。
创建项目安装依赖
$ 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
推荐阅读
-
深入理解spring boot异步调用方式@Async
-
Spring Boot利用@Async异步调用:使用Future及定义超时详解
-
从零学习node.js之详解异步控制工具async(八)
-
微信小程序中使用Async-await方法异步请求变为同步请求方法
-
ES7之Async/await的使用详解
-
在JS循环中使用async/await的方法
-
详解async/await 异步应用的常用场景
-
浅谈HTML5 defer和async的区别
-
Spring Boot利用@Async如何实现异步调用:自定义线程池
-
Spring Boot利用@Async异步调用:ThreadPoolTaskScheduler线程池的优雅关闭详解