Javascript异步编程(总结)

一、Generator

异步编程的方法

  • 回调函数
  • 事件监听
  • 发布/订阅
  • promise对象

什么是异步?

当前操作A执行一半时候去,操作b事件,一段时间后在操作A事件。(存在中断操作)

Generator 函数

Generator在ES6实现,可以交出函数的执行权(让函数暂时停止)

function* gen(x){
  var y = yield x + 2;
  return y;
}

整个Generator 函数就是一个异步容器,在异步操作需要暂停的时候,用yield 标记

var g = gen(1);
g.next() // { value: 3, done: false }
g.next() // { value: undefined, done: true }

执行 gen函数返回一个内部指针,返回的不是一个结果,需要调用next函数,移动函数指针,让指针指向下一个阶段(第一个遇到的yield ),返回的信息是一个对象包含了当前函数执行的value值,和done标记是否执行完毕。ture代表执行完毕,false戴代表还有下一阶段

Generator可以进行数据交换和错误处理

next上一次的处理结果返回

function* gen(x){
  var y = yield x + 2;
  return y;
}
var g = gen(1);
g.next() // { value: 3, done: false }
g.next(2) // { value: 2, done: true }

g.next(2) 会把参数传递进去,供下一个阶段来使用。

错误处理

function* gen(x){
  try {
    var y = yield x + 2;
  } catch (e){ 
    console.log(e);
  }
  return y;
}
var g = gen(1);
g.next();
g.throw'出错了';

g.throw代表着当前阶段出现了错误,g.throw错误码被try/catch捕获

Generator 函数的用法

var fetch = require('node-fetch');
function* gen(){
  var url = 'https://api.github.com/users/github';
  var result = yield fetch(url);
  console.log(result.bio);
}

//调用
var g = gen();
//移动指针
var result = g.next();
//返回promise结果
result.value.then(function(data){
	//拿到异步的值并返回
  return data.json();
}).then(function(data){
	//拿到上一个阶段的数据,传递进去console 就能打印数据啦
  g.next(data);
});

二、Thunk 函数的含义和用法

求值策略,即函数的参数到底应该何时求值。
求职策略分为两种,传值和传名调用,顾名思义,传值及传递一个处理好的值(如1+2,计算后传入,),传名,及把参数/函数名称传递,需要时在计算。

f(1+5)  ==>f(6)
f(1+5)===>f(1+5)*1

Thunk 函数的含义

编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。

function f(m){
  return m * 2;     
}
f(x + 5);
// 等同于
var thunk = function () {
  return x + 5;
};
function f(thunk){
  return thunk() * 2;
}
也就是将x+5 替换成  function (){retrun x+5} 这个函数在执行

JavaScript 语言的 Thunk 函数

JavaScript 语言是传值调用,它的 Thunk 函数含义有所不同。在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);
// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function (fileName){
  return function (callback){
    return fs.readFile(fileName, callback); 
  };
};

简单的 Thunk 函数转换器

const fs = require('fs')
 var Thunk = function (fn) {
     return function (){
         var args = Array.prototype.slice.call(arguments)
         return function (callback){
             args.push(callback)
              fn.apply(this.callback,args)
         }
     }
 }
function callback(err){
  console.log(err,'读取成功')
}
 //生成Thunk函数
var readFileThunk = Thunk(fs.readFile);
//传入callback 执行
readFileThunk('fileA')(callback)

Thunkify 模块

使用方法

var thunkify = require('thunkify');
var fs = require('fs');
var read = thunkify(fs.readFile);
read('package.json')(function(err, str){
  // ...
});

Thunkify 的源码确保了回调函数只运行一次。

Generator 函数的流程管理

 封装异步操作
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var gen = function* (){
  var r1 = yield readFile('/etc/fstab');
  console.log(r1.toString());
  var r2 = yield readFile('/etc/shells');
  console.log(r2.toString());
};


var g = gen();
var r1 = g.next();
r1.value(function(err, data){
  if (err) throw err;
  var r2 = g.next(data);
  r2.value(function(err, data){
    if (err) throw err;
    g.next(data);
  });
})

yield 命令用于将程序的执行权移出 Generator 函数,那么就需要一种方法,将执行权再交还给 Generator 函数。
这种方法就是 Thunk 函数,因为它可以在回调函数里,将执行权交还给 Generator 函数。

co 函数库的含义和用法

co 函数库是一个自执行的迭代器。

co函数用法

var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
var co = require('co');
co(gen).then(function (){
  console.log('Generator 函数执行完成');
})
//执行完毕输出执行完成

co函数原理

将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。以前在操作Generator 函数就是一个异步操作的容器,需要回调函数或者promise对象才能将结果返回。

基于 Promise 对象的自动执行

var fs = require('fs');

var readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) reject(error);
      resolve(data);
    });
  });
};

	//Generator 函数
var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
 //手动执行
var g = gen();
g.next().value.then(function(data){
  g.next(data).value.then(function(data){
    g.next(data);
  });
})
//自动执行
function run(gen){
  var g = gen();
  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }
  next();
}
run(gen);

co函数源码

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1)
  return new Promise(function(resolve, reject) {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    onFulfilled();
    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }
    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }
    function next(ret) {
      if (ret.done) return resolve(ret.value);
      var value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
  });
}

co调用时返回的是一个promise对象,在返回的promse对象中,首先判断gen是否个函数,如果是就执行该函数,得到一个内部指针对象;如果不是就返回,并将 Promise 对象的状态改为 resolved 。然后调用onFulfilled方法,如果出现错误会捕获该对象,没有错误直接调用next函数
,然后递归调用next函数返回结果。

async 函数的含义和用法

async 函数是什么?async 函数就是 Generator 函数的语法糖。

async 函数的实现

async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args){
  // ...
}
// 等同于
function fn(args){ 
  return spawn(function*() {
    // ...
  }); 
}

spawn 函数的实现

function spawn(genF) {
  return new Promise(function(resolve, reject) {
    var gen = genF();
    function step(nextF) {
      try {
        var next = nextF();
      } catch(e) {
        return reject(e); 
      }
      if(next.done) {
        return resolve(next.value);
      } 
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });      
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}
function fn(args) {
    return spawn(function* () {
        let a = 15;
        let b = 20;
        let c = yield Promise.resolve(5)
        return a+b+c
    });
}
let fns = fn()
fns.then((data)=>{
    console.log(data)
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值