我们都知道Js主线程是单线程的,所以异步处理在js中就显得尤为重要。异步处理的发展路程是:CallBack
回调处理——事件监听(类似jquery,on)——发布/订阅(Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件)
——Promise——generator(搭配co库)——ES7终极async,await。异步处理也从最初的横向发展的胖子到纵向发展
的大个子,到今天我们能像写同步代码一样写异步逻辑。
1.CallBack即“回调函数”
即异步操作执行完后触发执行的函数,例如:$.get("http://api.xxxx.com/xxx",callback);当请求完成时就
会触发callback函数。callback可以完成异步操作。
$.ajax({
url: "type",
data:1,
success: function (a) {
$.ajax({
url: "list",
data:a,
success: function (b) {
$.ajax({
url: "content",
data:b,
success: function (c) {
console.log(c)
}
})
}
})
}
})
这就是传说中的“回调地狱”,可读性差,不便于后期维护,易出错,横向发展的大胖子。
2、事件监听、发布/订阅。可以看看阮一峰老师的《Javascript异步编程的4种方法》:
http://www.ruanyifeng.com/blog/2012/12/asynchronous%EF%BC%BFjavascript.html
3.Promise
Promise就是为解决“回调地狱”而生的,。Promise对象简单说就是一个容器,里面保存着某个未来才会结束的事件
(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise
提供统一的 API,各种异步操作都可以用同样的方法进行处理。
const promise = new Promise(function(resolve, reject)
{
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,
由 JavaScript 引擎提供,不用自己部署。resolve函数的作用是,将Promise对象的状态从“未完成”变为
“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),
在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。Promise实例生成以后,
可以用then方法分别指定resolved状态和rejected状态的回调函数。promise.then(function(value) {
// success
}, function(error) {
// failure
});
then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,
第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。
这两个函数都接受Promise对象传出的值作为参数。
这样采用 Promise,解决“回调地狱”问题,比如node中连续读取多个文件,写法如下。
var readFile = require('fs-readfile-promise');
readFile(fileA)
.then(function (data) {
console.log(data.toString());
})
.then(function () {
return readFile(fileB);
})
.then(function (data) {
console.log(data.toString());
})
.catch(function (err) {
console.log(err);
});
可见这种写法要比CallBack写法直观的多。从横向变成了纵向发展。
4.Generator(一般要搭配co库,否则也是横向发展)
函数Genrator 函数要用* 来比标识,yield关键字表示暂停。将函数分割出好多个部分,调用一次next就会继续
向下执行。返回结果是一个迭代器,迭代器有一个next方法。
function* read() {
console.log(1);
let a = yield '123';
console.log(a);
let b = yield 9
console.log(b);
return b;
}
let it = read();
console.log(it.next('213')); // 1 {value:'123',done:false}
console.log(it.next('100')); // 100 {value:9,done:false}
console.log(it.next('200')); // 200 {value:200,done:true}
console.log(it.next('200')); // {value:200,done:true}
yield后面跟着的是value的值,next里面的参数会作为上一个yield的返回值,并且第一次next传值是无效的。
处理异步的时候Generator和Promise搭配使用。这里用到一个bluebird库,用来把一个异步方法转为Promise,
避免了每次需要在异步方法外new Promise()。
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);//将readFile转为Promise对象的实例
function* r() {
let content1 = yield read('./2.promise/1.txt', 'utf8');
let content2 = yield read(content1, 'utf8');
return content2;
}
这样看起来是我们想要的样子,但是只写成这样还不行,想得到r()的结果还要对函数进行处理
function co(it) { //co库的实现
return new Promise(function (resolve, reject) {
function next(d) {
let { value, done } = it.next(d);
if (!done) {
value.then(function (data) {
next(data)
}, reject)
} else {
resolve(value);
}
}
next();
});
}
co(r()).then(function (data) {
console.log(data)
})
5.async-await函数ES2017 标准引入了 async 函数,使得异步操作变得更加方便。async 函数是什么?
一句话,它就是 Generator 函数的语法糖。
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
async function r(){
try{
let content1 = await read('./2.promise/100.txt','utf8');
let content2 = await read(content1,'utf8');
return content2;
}catch(e){ // 如果出错会catch
console.log('err',e)
}
}
async函数其实就是将 Generator 函数的星号(*)替换成async,将yield替换成await。
async函数返回的是也是promiser().then(function(data){
console.log(data);
},function(err){
})