JavaScript异步发展史

JavaScript 的全部网络操做,浏览器事件,都必须是异步执行。 正如咱们所知道的那样,在JavaScript中,异步编程方式只能经过JavaScript语言中的一等公民函数才能完成:这种方式意味着咱们能够将一个函数做为另外一个函数的参数,在这个函数的内部能够调用被传递进来的函数(即回调函数)。html

let fs = require('fs');
 fs.readFile('./1.txt','utf8',function(err,data){
 if(err){//若是err有值,就表示程序出错了
 console.log(err);
 }else{//若是error为空,就表示 成功了,没有错误
 console.log(data);
 }
 })
复制代码

回调函数的问题

    1. 没法捕获错误 try catch
    1. 不能return
    1. 回调地狱
function read(filename){
 fs.readFile(filename,'utf8',function(err,data){
 throw Error('出错了')
 if(err){//若是err有值,就表示程序出错了
 console.log(err);
 }else{//若是error为空,就表示 成功了,没有错误
 console.log(data);
 }
 })
 }
 try{
 read('./1.txt');
 }catch(e){
 console.log('err',e);
 };
复制代码

当你访问服务器的时候,好比要请求一个HTML页面,好比是用户列表。服务器一方面会去读取读模板文件,多是ejs pug jade handlebar ,另一方面还要读取数据(可能会放在文件里,也能够会放在数据里),它们都很慢,因此都是异步的。node

这种恶魔金字塔有如下问题

    1. 很是难看
    1. 很是难以维护
    1. 效率比较低,由于它们是串行的
fs.readFile('./template.txt', 'utf8', function (err, template) {
  fs.readFile('./data.txt', 'utf8', function (err, data) {
    console.log(template + ' ' + data);
  })
})
复制代码

如何解决这个回调嵌套的问题

1.经过事件发布订阅来实现 这是node核心模块中的一个类,经过它能够建立事件发射器的实例,里面有两个核心方法,一个叫on emit,on表示注册监听,emit表示发射事件ajax

let EventEmitter = require('events');
let eve = new EventEmitter();
//这个html对象是存放最终数据
let html = {};//template data
//监听数据获取成功事件,当事件发生以后调用回调函数
eve.on('ready',function(key,value){
  html[key] = value;
  if(Object.keys(html).length == 2){
    console.log(html);
  }
});
fs.readFile('./template.txt', 'utf8', function (err, template) {
  //1事件名 2参数日后是传递给回调函数的参数
  eve.emit('ready','template',template);
})
fs.readFile('./data.txt', 'utf8', function (err, data) {
  eve.emit('ready','data',data);
})*/
//经过一个哨兵函数来处理
function done(key,value){
  html[key] = value;
  if(Object.keys(html).length == 2){
    console.log(html);
  }
}
复制代码

ES6的不少特性都跟Generator扯上关系,并且实际用处比较广, 包含了任何须要异步的模块, 好比ajax, filesystem, 或者数组对象遍历等均可以用到; Generator函数和普通的函数区别有两个, 1:function和函数名之间有一个*号, 2:函数体内部使用了yield表达式;好比这样:编程

/**
 * 生成器是一个函数,能够用来生成迭代器
 * 生成器函数和普通函数不同,普通函数是一旦调用必定会执行完
 * 可是生成器函数中间能够暂停,能够执行一会歇一会
 */
//生成器函数有一个特色,须要加个*
//生成器有若干个阶段,如何划分这些阶段呢?
function *go(a){
    console.log(1);
    //此处的b用来供外界输入进来的
    //这一行实现输入和输出,本次的输出放在yield后面,下次的输入放在yield前面
    let b =  yield a;
    console.log(2);
    let c = yield b;
    console.log(3);
    return c;
}
//生成器函数和普通的函数不同,调用它的话函数并不会马上执行
//它会返回今生成器的迭代器,迭代器是一个对象,每调用一次next就能够返回一个值对象
let it = go("a值");
//next第一次执行不须要参数,传参数没有意义
let r1 = it.next();
//第一次调用next会返回一个对象,此对象有两个属性,一个是value就是yield后面那个值,一个是done表示是否迭代完成
console.log(r1);//{ value: 'a', done: false }
let r2 = it.next('B值');
console.log(r2);//{ value: 'B值', done: false }
let r3 = it.next('C值');
console.log(r3);//{ value: 'C值', done: true }
复制代码

!重点来了

先回忆以前promise对异步的实现,以 bluebird为例:数组

let Promise = require('bluebird');
let fs = require('fs');
function promisifyAll(obj) {
  for (let key in obj) {
    if (obj.hasOwnProperty(key) && typeof obj[key] == 'function') {
      obj[key+'Async'] = Promise.promisify(obj[key]);
    }
  }
}
//它会遍历对象上面的全部方法,而后对每一个方法添加一个新的方法 Async
promisifyAll(fs);
fs.readFileAsync('./1.txt', 'utf8').then(data => console.log(data));
复制代码

如今将Generator与promise综合在一块儿:

let fs = require('fs');
function readFile(filename) {
 return new Promise(function (resolve, reject) {
   fs.readFile(filename, 'utf8', function (err, data) {
     err ? reject(err) : resolve(data);
   });
 })
}
function *read() {
 console.log('开始');
 let a = yield readFile('1.txt');
 console.log(a);
 let b = yield readFile('2.txt');
 console.log(b);
 let c = yield readFile('3.txt');
 console.log(c);
 return c;
}
function co(gen) {
 let it = gen();//咱们要让咱们的生成器持续执行
 return new Promise(function (resolve, reject) {
   !function next(lastVal) {
       let {value,done} = it.next(lastVal);
       if(done){
         resolve(value);
       }else{
         value.then(next,reject);
       }
   }()
 });
}
co(read).then(function (data) {
 console.log(data);
});
复制代码

随着 Node 7的发布,愈来愈多的人开始研究听说是异步编程终级解决方案的 async/await

let Promise = require('bluebird');
let readFile = Promise.promisify(require('fs').readFile);
async function read() {
  //await后面必须跟一个promise,
  let a = await readFile('./1.txt','utf8');
  console.log(a);
  let b = await readFile('./2.txt','utf8');
  console.log(b);
  let c = await readFile('./3.txt','utf8');
  console.log(c);
  return 'ok';
}

read().then(data => {
  console.log(data);
});
复制代码

async await是语法糖,内部仍是用generator+promise实现promise

相关文章
相关标签/搜索