JavaScript体验异步更好的解决办法


Posted in Javascript onJanuary 08, 2018

一、异步解决方案的进化史
JavaScript的异步操作一直是个麻烦事,所以不断有人提出它的各种解决方案。可以追溯到最早的回调函数(ajax老朋友),到Promise(不算新的朋友),再到ES6的Generator(强劲的朋友)。
几年前我们可能用过一个比较著名的Async.js,但是它没有摆脱回调函数,并且错误处理也是按照“回调函数的第一个参数用来传递错误”这样一个约定。而众所周知的回调地狱仍然是一个比较突出的问题,直到Generator改变了这种异步风格。
但是ES7的async await的出现(碉堡的新朋友),我们可以轻松写出同步风格的代码同时又拥有异步机制,可以说是目前最简单,最优雅,最佳的解决方案了。

二、async await语法
async await语法比较简单,可以认为是Generator的语法糖,比起星号和yield更具有语义化。下面一个简单的例子表示1秒之后输出hello world:

function timeout(ms) {
 return new Promise((resolve) => {
  setTimeout(resolve, ms);
 });
}
async function asyncPrint(value, ms) {
 await timeout(ms);
 console.log(value)
}
asyncPrint('hello world', 1000);

await只能用在async函数中,如果用在普通函数就会报错

await后面跟的是一个Promise对象(当然其它值也可以,但是会包装成一个立即resolve的Promise,也就没有意义了)

await会等待Promise的结果返回再继续执行

await等待的虽然是Promise对象,但是不必写.then(),直接可以得到返回值,将上面的代码微调,发现返回值result也是可以输出hello world:

function timeout(ms) {
 return new Promise((resolve) => {
  setTimeout(_ => {resolve('hello world')}, ms);
 });
}
async function asyncPrint(ms) {
 let result = await timeout(ms);
 console.log(result)
}
asyncPrint(1000);

三、async await错误处理

前面说了await等待的虽然是Promise对象,但是不必写.then(),所以其实也不用写.catch()了,直接用try catch就能捕捉错误,这样可以避免错误处理代码非常冗余和笨重,还是将上面的例子微调:

function timeout(ms) {
 return new Promise((resolve, reject) => {
  setTimeout(_ => {reject('error')}, ms);//reject模拟出错,返回error
 });
}
async function asyncPrint(ms) {
 try {
   console.log('start');
   await timeout(ms);//这里返回了错误
   console.log('end');//所以这句代码不会被执行了
 } catch(err) {
   console.log(err); //这里捕捉到错误error
 }
}
asyncPrint(1000);

如果有多个await,可以一起放在try catch中:

async function main() {
 try {
  const async1 = await firstAsync();
  const async2 = await secondAsync();
  const async3 = await thirdAsync();
 }
 catch (err) {
  console.error(err);
 }
}

四、async await注意点

1). 前面已经说过,await命令后面的Promise对象,运行结果很可能是reject或逻辑报错,所以最好把await放在try catch代码块中。

2). 多个await命令的异步操作,如果不存在依赖关系,让它们同时触发。

const async1 = await firstAsync();
const async2 = await secondAsync();

上面代码中,async1和async2如果是两个独立的异步操作,这样写会比较耗时,因为只有firstAsync完成以后,才会执行secondAsync,完全可以用Promise.all优雅地处理:

let [async1, async2] = await Promise.all([firstAsync(), secondAsync()]);

3). await只能用在async函数之中,如果用在普通函数就会报错:

async function main() {
 let docs = [{}, {}, {}];
 //报错 await is only valid in async function
 docs.forEach(function (doc) {
  await post(doc);
  console.log('main');
 });
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

在forEach内部方法加上async就可以了:

async function main() {
 let docs = [{}, {}, {}];
 docs.forEach(async function (doc) {
  await post(doc);
  console.log('main');
 });
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

但是你会发现3个main是同时输出的,这就说明post是并发执行的,而不是继发执行,改成for就可以解决问题,3个main是分别相隔1秒输出:

async function main() {
 let docs = [{}, {}, {}];
 for (let doc of docs) {
  await post(doc);
  console.log('main');
 }
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

总之,用了async await之后整个人神清气爽,可以用非常简洁和优雅的代码实现各种花式异步操作,并且在业务逻辑复杂的情况下可以不用陷入回调地狱中。不敢说这一定是终极的解决方案,但确实是目前最优雅的解决方案!

Javascript 相关文章推荐
我遇到的参数传递中 双引号单引号嵌套问题
Feb 11 Javascript
javascript客户端解决方案 缓存提供程序
Jul 14 Javascript
如何创建一个JavaScript弹出DIV窗口层的效果
Sep 25 Javascript
node.js中的fs.readSync方法使用说明
Dec 17 Javascript
JavaScript动态创建form表单并提交的实现方法
Dec 10 Javascript
JavaScript+canvas实现七色板效果实例
Feb 18 Javascript
简单讲解AngularJS的Routing路由的定义与使用
Mar 05 Javascript
vue2.0 keep-alive最佳实践
Jul 06 Javascript
Angular自定义组件实现数据双向数据绑定的实例
Dec 11 Javascript
Vue v-for循环之@click点击事件获取元素示例
Nov 09 Javascript
一文秒懂JavaScript构造函数、实例、原型对象以及原型链
Aug 25 Javascript
利用promise及参数解构封装ajax请求的方法
Mar 24 Javascript
探索Vue高阶组件的使用
Jan 08 #Javascript
Vue入门之数据绑定(小结)
Jan 08 #Javascript
浅谈Vue数据绑定的原理
Jan 08 #Javascript
让网站自动生成章节目录索引的多个js代码
Jan 07 #Javascript
JavaScript学习总结(一) ECMAScript、BOM、DOM(核心、浏览器对象模型与文档对象模型)
Jan 07 #Javascript
Bootstrap treeview实现动态加载数据并添加快捷搜索功能
Jan 07 #Javascript
vue实现登录后页面跳转到之前页面
Jan 07 #Javascript
You might like
分页显示Oracle数据库记录的类之一
2006/10/09 PHP
PHP之数组学习
2011/05/29 PHP
PHP预定义超全局数组变量小结
2018/08/20 PHP
用javascript控制iframe滚动的代码
2007/04/10 Javascript
让mayfish支持mysqli数据库驱动的实现方法
2010/05/22 Javascript
FireFox下XML对象转化成字符串的解决方法
2011/12/09 Javascript
JQuery each打印JS对象的方法
2013/11/13 Javascript
js弹出div并显示遮罩层
2014/02/12 Javascript
jquery仿搜索自动联想功能代码
2014/05/23 Javascript
浅析Node.js查找字符串功能
2014/09/03 Javascript
Javascript中this关键字的一些小知识
2015/03/15 Javascript
基于JavaScript实现快速转换文本语言(繁体中文和简体中文)
2016/03/07 Javascript
jQuery实现内容定时切换效果完整实例
2016/04/06 Javascript
javascript获取select标签选中的值
2016/06/04 Javascript
jQuery EasyUI基础教程之EasyUI常用组件(推荐)
2016/07/15 Javascript
JavaScript表单焦点自动切换代码
2016/07/24 Javascript
Vue获取DOM元素样式和样式更改示例
2017/03/07 Javascript
Javascript前端经典的面试题及答案
2017/03/14 Javascript
微信小程序教程系列之视图层的条件渲染(10)
2017/04/19 Javascript
解决canvas画布使用fillRect()时高度出现双倍效果的问题
2017/08/03 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
vue中的mescroll搜索运用及各种填坑处理
2019/10/30 Javascript
Python 字符串中的字符倒转
2008/09/06 Python
Python数据类型详解(一)字符串
2016/05/08 Python
利用Python为iOS10生成图标和截屏
2016/09/24 Python
Python实现ping指定IP的示例
2018/06/04 Python
Python数据可视化教程之Matplotlib实现各种图表实例
2019/01/13 Python
python 接口实现 供第三方调用的例子
2019/08/13 Python
python 使用建议与技巧分享(四)
2020/08/18 Python
python绘图pyecharts+pandas的使用详解
2020/12/13 Python
澳大利亚时尚前卫设计师珠宝在线:Amber Sceats
2017/10/04 全球购物
英国领先的独立时装店:Van Mildert
2019/10/28 全球购物
销售主管的自我评价分享
2014/01/03 职场文书
《王二小》教学反思
2014/02/27 职场文书
运动会拉拉队口号
2014/06/09 职场文书
公司庆典主持词
2015/07/04 职场文书