在javascript单线程的世界里,没有异步步履维艰。本章节介绍异步编程的发展,从callback
,Events
到promise
,generator
,async/await
.javascript
在(javascript)单线程的世界里,若是有多个任务,就必须排队,前面一个完成,再继续后面的任务。就像一个ATM排队取钱似的,前面一个不取完,就不让后面的取。 为了这个问题,javascript提供了2种方式: 同步和异步。 异步就像银行取钱填了单子约了号,等着叫到号,再去作取钱,等待的时间里还能够干点其余的事儿~html
举个例子:经过api拿到数据,数据里面有图片,图片加载成功渲染,那么代码以下:java
// 伪代码
request(url, (data) => {
if(data){
loadImg(data.src, () => {
render();
})
}
})
复制代码
若是有在业务逻辑比较复杂或者NodeJS I/O操做比较频繁的时候,就成了下面这个样子:node
doSth1((...args, callback) => {
doSth2((...args, callback) => {
doSth3((...args, callback) => {
doSth4((...args, callback) => {
doSth5((...args, callback) => {
})
})
})
})
})
复制代码
这样的维护性
和可读性
,整我的瞬间感受很差了~jquery
try {
setTimeout(() => {
throw new Error('unexpected error');
}, 100);
} catch (e) {
console.log('error2', e.message);
}
复制代码
以上代码运行抛出异常,但try catch不能获得将来时间段的异常。git
流程控制只能经过维护各类状态
来处理,不利于管理es6
无论浏览器仍是NodeJS,提供了大量内置事件API来处理异步。github
浏览器中如: websocket
, ajax
, canvas
, img
,FileReader
等 NodeJS如: stream
, http
等web
EventEmitter
事件模型addEventListener
,此外浏览器也提供一些自定义事件的API,但兼容性很差,具体能够这篇文章;也能够用Node中的EventEmitter
;jquery
中也对此作了封装,on
,bind
等方法支持自定义事件。事件必定程度上解决了解耦
和提高了代码可维护性
;对于异常处理
,只有部分支持相似error事件
才能处理。若想实现异常处理机制,只有本身模拟error事件,比较繁琐。ajax
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是ES6提供的方法,是生成器,最大的特色:能够暂停执行和恢复执行(能够交出函数的执行权),返回的是指针对象
.
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介绍和应用
ES7提供了async
函数,使得异步操做变得更加方便。
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.0
发布,全面支持 async/await
, 推荐使用 async/await
, 低版本node能够使用 babel
来编译处理。 而 为了方便 接口设计时 返回 promise
更方面使用者. 固然依然使用 callback
, 经过 promisify
作转换, Node8.0
已经内置 util.promisify
方法。
异步编程
在javascript中扮演者重要的角色,虽然如今须要经过babel
,typescript
等编译或转换代码,跟着规范
和标准
走,就没有跑偏。
很久以前在github博客上的文章了。
如需转载,请备注出处。