异步编程小结
栏目: JavaScript · 发布时间: 5年前
内容简介:在javascript单线程的世界里,没有异步寸步难行。本章节介绍异步编程的发展,从在(javascript)单线程的世界里,如果有多个任务,就必须排队,前面一个完成,再继续后面的任务。就像一个ATM排队取钱似的,前面一个不取完,就不让后面的取。 为了这个问题,javascript提供了2种方式: 同步和异步。 异步就像银行取钱填了单子约了号,等着叫到号,再去做取钱,等待的时间里还可以干点其他的事儿~举个例子:通过api拿到数据,数据里面有图片,图片加载成功渲染,那么代码如下:
在javascript单线程的世界里,没有异步寸步难行。本章节介绍异步编程的发展,从 callback
, Events
到 promise
, generator
, async/await
.
为什么要使用异步编程
在(javascript)单线程的世界里,如果有多个任务,就必须排队,前面一个完成,再继续后面的任务。就像一个ATM排队取钱似的,前面一个不取完,就不让后面的取。 为了这个问题,javascript提供了2种方式: 同步和异步。 异步就像银行取钱填了单子约了号,等着叫到号,再去做取钱,等待的时间里还可以干点其他的事儿~
异步回调带来的问题
回调地狱 (Callbacks Hell)
举个例子:通过api拿到数据,数据里面有图片,图片加载成功渲染,那么代码如下:
// 伪代码 request(url, (data) => { if(data){ loadImg(data.src, () => { render(); }) } }) 复制代码
如果有在业务逻辑比较复杂或者NodeJS I/O操作比较频繁的时候,就成了下面这个样子:
doSth1((...args, callback) => { doSth2((...args, callback) => { doSth3((...args, callback) => { doSth4((...args, callback) => { doSth5((...args, callback) => { }) }) }) }) }) 复制代码
这样的 维护性
和 可读性
,整个人瞬间感觉不好了~
异常处理
try { setTimeout(() => { throw new Error('unexpected error'); }, 100); } catch (e) { console.log('error2', e.message); } 复制代码
以上代码运行抛出异常,但try catch不能得到未来时间段的异常。
流程控制不方便
流程控制只能通过维护各种 状态
来处理,不利于管理
异步编程现有解决方案对比
事件机制
不管浏览器还是NodeJS,提供了大量内置事件API来处理异步。
事件监听
浏览器中如: websocket
, ajax
, canvas
, img
, FileReader
等 NodeJS如: stream
, http
等
自定义事件(本质上是一种发布订阅模式)
- NodeJS中的
EventEmitter
事件模型 - 浏览器中:如DOM可使用
addEventListener
,此外浏览器也提供一些自定义事件的API,但兼容性不好,具体可以这篇文章;也可以用Node中的EventEmitter
;jquery
中也对此做了封装,on
,bind
等方法支持自定义事件。
事件小结
事件一定程度上解决了 解耦
和提升了代码 可维护性
;对于 异常处理
,只有部分支持类似 error事件
才能处理。若想实现异常处理机制,只有自己模拟error事件,比较繁琐。
Promise
Promise严格来说不是一种新技术,它只是一种机制,一种代码结构和流程,用于管理异步回调。为了统一规范产生一个Promise/A+规范,点击查看Promise/A+中文版,cnode的 William17
实现了Promise/A+规范,有兴趣的可以点这里查看
-
promise
状态由内部控制,外部不可变 - 状态只能从
pending
到resovled
,rejected
,一旦进行完成不可逆 - 每次
then/catch
操作返回一个promise实例,可以进行链式操作
部分代码如下:
readFile(path1).then(function (data) { console.log(data.toString()); return readFile(path2); }).then(function (data) { console.log(data.toString()); return readFile(errorPath); }).then(function (data) { console.log(data.toString()); }).catch(function (e) { console.log('error', e); return readFile(path1); }).then(function (data) { console.log(data.toString()); }); 复制代码
Promise的缺陷:
promise.catch
Generator
generator介绍
Generator是ES6提供的方法,是生成器,最大的特点:可以暂停执行和恢复执行(可以交出函数的执行权),返回的是 指针对象
.
- 需要自执行函数才能持续运行,否则需要手工调用流程
- 自执行函数借助promise, 获取异常和最终结果
generator 和 promise自执行实现
const run = function (generator) { var g = generator() var perform = function (result) { if (result.done === true) { return result.value } if (isPromise(result.value)) { return result.value.then(function (v) { return perform(g.next(v)) }).catch(function (e) { return perform(g.throw(e)) }) } else { return perform(g.next(result.value)) } } return perform(g.next()) } const isPromise = f => f.constructor === Promise function* g() { var a = yield sleep(1000, _ => 1) var b = yield sleep(1000, _ => 2) return a + b } function sleep(d, fn) { return new Promise((resolve, reject) => { setTimeout(_ => resolve(fn()), d) }) } 复制代码
由于以上问题,于是一个叫 co 库诞生,支持 thunk
和 Promise
. 关于thunk可以查看 阮一峰老师的thunk介绍和应用
Async/Await
ES7提供了 async
函数,使得异步操作变得更加方便。
- 内置执行器
- 更好的语义
- 更多的实用场景,co参数Generator函数中
yield
只能是promise
和thunk
实例代码:
async function asyncReadFile() { var p1 = readFile(path.join(__dirname, '../data/file1.txt')); var p2 = readFile(path.join(__dirname, '../data/file2.txt')); var [f1, f2] = await Promise.all([p1, p2]); return `${f1.toString()}\n${f2.toString()}`; } (async function () { try { console.log(await asyncReadFile()); } catch (e) { console.log(e.message) } })(); 复制代码
Node8 async/await 支持
Node8.0
发布,全面支持 async/await
, 推荐使用 async/await
, 低版本node可以使用 babel
来编译处理。 而 为了方便 接口设计时 返回 promise
更方面使用者. 当然依然使用 callback
, 通过 promisify
做转换, Node8.0
已经内置 util.promisify
方法。
参考资料
小结
异步编程
在javascript中扮演者重要的角色,虽然现在需要通过 babel
, typescript
等编译或转换代码,跟着 规范
和 标准
走,就没有跑偏。
好久之前在github博客上的文章了。
如需转载,请备注出处。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。