JS异步解决方案
前言
异步最早的解决方案是回调函数,如ajax,事件的回调,setInterval/setTimeout中的回调。但是回调函数有回调地狱的问题;
为了解决回调地狱的问题,社区提出了Promise解决方案,ES6将其写进了语言标准。Promise一定程度上解决了回调地狱的问题,但是Promise也存在一些问题,如错误不能被try catch,而且使用Promise的链式调用,其实并没有从根本上解决回调地狱的问题,只是换了一种写法。
ES6中引入 Generator 函数,Generator是一种异步编程解决方案,Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权,Generator 函数可以看出是异步任务的容器,需要暂停的地方,都用yield语句注明。但是 Generator 使用起来较为复杂。
ES7又提出了新的异步解决方案:async/await,async是 Generator 函数的语法糖,async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样。
同步和异步
关于同步和异步的概念,这里给出阮一峰老师的文章参考。其中也不乏一些异步的解决办法例子,如《js异步编程的4种方法》,《异步操作概述》。
异步解决方案
回调函数
// node读取文件
fs.readFile('url', 'utf-8', function(err, data) {
// code
});
使用的场景有:ajax请求,事件回调函数,Node API,定时器等。
优点: 简单。 缺点: 异步回调嵌套会导致代码难以维护,并且不方便统一处理错误,不能 try,catch 和存在回调地狱问题。
// 回调地狱问题,以读取A文本内容,再根据A文本内容读取B再根据B的内容读取C..为例
fs.readFile('A', 'utf-8', function(err, data) {
fs.readFile('B', 'utf-8', function(err, data) {
fs.readFile('C', 'utf-8', function(err, data) {
fs.readFile('D', 'utf-8', function(err, data) {
// code
});
});
});
});
Promise
Promise 一定程度上解决了回调地狱的问题,Promise 最早由社区提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。《ECMAScript 6 入门》
我们来观察一下Promise是如何解决回调地狱问题的。
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
});
});
}
read('A').then(data => {
return read('B');
}).then(data => {
return read('C');
}).then(data => {
return read('D');
}).catch(err => {
console.log(err);
});
promise的优点: 1.一旦状态改变,就不会再变,任何时候都可以得到这个结果。2.可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
promise的缺点:1.无法取消 Promise。 2.当处于pending状态时,无法得知目前进展到哪一个阶段。 3.错误不能被 trycatch
这里举一个在promise之前,读取A,B,C三个文件内容,都读取成功后,再输出最终的结果例子。依赖于发布订阅模式
let pubsup = {
arry: [],
emit: function() {
this.arry.forEach(fn => fn());
},
on: function(fn) {
this.arry.push(fn);
}
};
let data = [];
pubsub.on(() => {
if (data.length == 3) {
console.log(data);
}
});
fs.readFile('A', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});
fs.readFile('B', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});
fs.readFile('C', 'utf-8', function(err, data) {
data.push(data);
pubsup.emit();
});
顺便提一下朴灵的深入浅出nodejs里面介绍不同场景下异步获取数据进行操作的实践,非常值得看。
Promise给我们提供了 Promise.all 的方法,对于这个需求,我们可以使用 Promise.all 来实现。
/**
* 将 fs.readFile 包装成promise接口
*/
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
});
});
}
/**
* Promise.all 可以实现多个异步并行执行,同一时刻获取最终结果的问题
*/
Promise.all([
read(A),
read(B),
read(C)
]).then(data => {
console.log(data);
}).catch(err => console.log(err));
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案,整个 Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。异步操作需要暂停的地方,都用 yield 语句注明。
Generator 函数一般配合 yield 或 Promise 使用。Generator函数返回的是迭代器。对生成器和迭代器不了解的同学参考下阮一峰的ES6入门
function* generator() {
let a = yield 111;
console.log(a);
let b = yield 222;
console.log(b);
let c = yield 333;
console.log(c);
let d = yield 444;
console.log(d);
}
let t = generator();
// next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值
t.next(1); // 第一次调用next函数时,传递的参数无效
t.next(2); // 输出2
t.next(3); //
t.next(4); //
t.next(5); //
仍然以上文的 readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C)为例,使用 Generator + co库来实现:
const fs = require('fs');
const co = require('co');
const bluebird = require('bluebird');
const readFile = bluebird.promiseify(fs.readFile);
function* read() {
yield readFile('A', 'utf-8');
yield readFile('B', 'utf-8');
yield readFile('C', 'utf-8');
}
co(read()).then(data => {
// code
}).catch(err => {
// code
});
await/async
ES7中引入了 async/await 概念。async 其实是一个语法糖,它的实现就是将 Generator函数和自动执行器(co),包装在一个函数中。
async/await 的优点是代码清晰,不用像 Promise 写很多 then 链,就可以处理回调地狱的问题。并且错误可以被try catch。
仍然以上文的readFile (先读取A文本内容,再根据A文本内容读取B再根据B的内容读取C) 为例,使用 async/await 来实现:
const fs = require('fs');
const co = require('co');
const bluebird = require('bluebird');
const readFile = bluebird.promiseify(fs.readFile);
async function read() {
await readFile('A', 'utf-8');
await readFile('B', 'utf-8');
await readFile('C', 'utf-8');
}
co(read()).then(data => {
// code
}).catch(err => {
// code
});
我们用async/await来实现同样的效果。
function read(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, 'utf-8', function(err, data) {
if (err) reject(err);
resolve(data);
})
});
}
async function readAsync() {
const data = await Promise.all([
read(A),
read(B),
read(C)
]);
return data;
}
readSync().then(data => {
console.log(data);
})
所以JS的异步发展史,可以认为是从 callback -> promise -> generator -> async/await。async/await 使得异步代码看起来像同步代码,异步编程发展的目标就是让异步逻辑的代码看起来像同步一样
JS异步解决方案的更多相关文章
- JS异步解决方案之概念理解-----------阻塞和非阻塞,同步和异步,并发和并行,单线程和多线程
首先记住一句话,JS是单线程的. 单线程意味着什么?单线程意味着 它不能依靠自己实现异步. JS实现的异步,往往都是靠 浏览器.Node 的机制(事件驱动.回调)实现的. 下面让我这个单身狗 以谈恋爱 ...
- node.js异步编程解决方案之Promise用法
node.js异步编程解决方案之Promise var dbBase = require('../db/db_base'); var school_info_db = require('../db/s ...
- js async await 终极异步解决方案
既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Pr ...
- 关于使用Iscroll.js异步加载数据后不能滑动到最底端的问题解决方案
关于使用Iscroll.js异步加载数据后不能滑动到最底端,拉到最下边又弹回去的问题困扰了我老半天,相信很多朋友都遇到了.我刚好不小心解决了,和大家分享一下.由于各种忙,下边就直接上代码吧. (前提是 ...
- [转] js async await 终极异步解决方案
阅读目录 回顾 Promise async await 字面理解 async.await 如何执行 await 操作符 总结 既然有了promise 为什么还要有async await ? 当然是pr ...
- js异步编程
前言 以一个煮饭的例子开始,例如有三件事,A是买菜.B是买肉.C是洗米,最终的结果是为了煮一餐饭.为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A.B做完),这三件事是相关的, ...
- 关于JS异步加载方案
javascript延迟加载的解决方案: 1.使用defer标签 <span style="font-size: small;"><script type=&qu ...
- js 异步流程控制之 avQ(avril.queue)
废话前言 写了多年的js,遇到过最蛋疼的事情莫过于callback hell, 相信大家也感同身受. 业界许多大大也为此提出了很多不错的解决方案,我所了解的主要有: 朴灵 event proxy, 简 ...
- 前端分享----JS异步编程+ES6箭头函数
前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...
随机推荐
- oracle控制何时触发审计动作
1)By session / By Access by session对每个session中发生的重复操作只记录一次 by access对每个session中发生的每次操作都记录,而不管是否重复. 对 ...
- Oracle中事物处理--事物隔离级别
n 事物隔离级别 概念:隔离级别定义了事物与事物之间的隔离程度. ANSI/ISO SQL92标准定义了一些数据库操作的隔离级别(这是国际标准化组织定义的一个标准而已,不同的数据库在实现时有所不同) ...
- PHPCMS快速建站系列之添加单页模版
单页模板命名:page_xxx.html 以page_开头 在模版所在目录的config.php中添加配置项 'page_xxx.html' => '单网页', 也可以不在config中配置,不 ...
- 阿里云的重大战略调整,“被集成”成核心,发布SaaS加速器助力企业成长
摘要: 阿里云战略调整,“被集成”成为生态战略,讲讲即将“退居幕后”的阿里云. 阿里云近期调整动作巨大,阿里云新任总裁张剑锋(花名,行颠)上任后充分体现其创新和自我探索不断求“变”的阿里特性.期间,达 ...
- 安装软件时候出现"无效驱动器D"
安装软件的时候,出现以下问题. 如图: 无效驱动器 原因是因为之前安装过这样的软件在H盘,后期更改没了H,所以出现了错误. 解决方案: 打开注册表,搜索软件的关键字如 vmware 删除错误路径即 ...
- JavaScript--tab栏切换效果
tab栏切换效果: <!DOCTYPE html> <html> <head lang="en"> <meta charset=" ...
- C++:for范围循环特点--自我理解
for(declaration : expression)statement for(xx-type i : P)....其一:for范围类型循环在循环前,可能会对p所在的队列里,对每一个对象进行一次 ...
- myeclipse的最有用的设置
1 取消Myeclipse的自动文件验证(卡傻的原因) Windows –> Perferences –>Myeclipse –> Validation,保留manual(手动) ...
- 2019-9-23-dotnet-判断特定进程存在方法
title author date CreateTime categories dotnet 判断特定进程存在方法 lindexi 2019-09-23 16:20:42 +0800 2019-09- ...
- SDUT-2138_判断可达性
数据结构实验之图论三:判断可达性 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 在古老的魔兽传说中,有两个军团,一个叫 ...