美文网首页
JavaScript异步编程

JavaScript异步编程

作者: 学的会的前端 | 来源:发表于2019-10-09 10:32 被阅读0次

什么是异步

所谓"异步",简单说就是一个任务分成两段,先执行第一段,然后转而执行其他任务,等做好了准备,再回过头执行第二段。

ES5的异步编程方法

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

回调函数

  • 含义
    所谓回调函数(callback),就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数。
  • 代码示例
 readflie(/etc,function(err,data){
        if (err) throw err;
        console.log(data);
    })
  • 回调函数的缺点:
    当回调函数比较多时,容易出现回调函数的嵌套。(回调函数噩梦)

Promise 对象

  • promise含义
    它不是新的语法功能,而是一种新的写法
  • 代码示例
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);
});
  • promise缺点:
    代码冗余。

协程

  • 协程含义
    多个线程互相协作,完成异步任务。
  • 协程代码
function asnycJob() {
  // ...其他代码
  var f = yield readFile(fileA);
  // ...其他代码
}
//上面代码的函数 asyncJob 是一个协程,它的奥妙就在其中的 yield 命令。它表示执行到此处,执行权将交给其他协程。也就是说,yield命令是异步两个阶段的分界线。
  • 协程优点:
    代码的写法非常像同步操作,去掉yield后,和同步一样。

ES6的异步编程方法

Generator函数

  • Generator函数含义:
    Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。它可以实现暂停执行和恢复执行,是实现异步的根本原因。
  • 代码示例
 function *gen(x){ 
        var y = yield x + 1
        return y
    }

运行结果截图


捕获.PNG
  • Generator函数不同于其他函数的地方
    即执行它不会返回结果,返回的是指针对象。调用指针 a 的 next 方法,会移动内部指针(即执行异步任务的第一段),指向第一个遇到的 yield 语句,上例是执行到 x + 1 为止。
  • next方法的作用:
    next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)。value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。当done为true时,表示没有下一个阶段。
  • Generator函数的三大特点:
  1. 暂停执行和恢复执行
  2. 函数体内外的数据交换
  3. 错误处理机制。
  • 具体代码和截图
  1. next 方法返回值的 value 属性,是 Generator 函数向外输出数据;next 方法还可以接受参数,这是向 Generator 函数体内输入数据。
  2. Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
    function *gen(x){
        try{
            var y = yield x + 1
            return y
        }catch(e){
            console.log(e)
        }
    }
捕获.PNG
  • 虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)

Thunk 函数

  • Thunk 函数含义
    编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。它是"传名调用"的一种实现策略,用来替换某个表达式。
  • 代码示例
function f(m){
        return m * 5
    }
f(x + 4)


 //thunk函数
var thunk = function(){
     return x + 4
}
function f(thunk){
    return thunk() * 5
}
  • JavaScript 语言的 Thunk 函数
    JavaScript 语言是传值调用,它的 Thunk 函数含义有所不同。在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。
   readFile(ileName,callback);
    
    var readFileThunk = Thunk(fileName)
    readFileThunk(callback)
    
    
    var Thunk = functin(fileName){
        return function(callback){
            return readFile(fileName, callback);
        }
    }
  • Thunk 函数作用:
    Thunk 函数现在可以用于 Generator 函数的自动流程管理。Thunk 函数真正的威力,在于可以自动执行 Generator 函数

function run(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

run(gen);

co 函数库

  • 什么是co函数库
    co 函数库是著名程序员 TJ Holowaychuk 于2013年6月发布的一个小工具,用于 Generator 函数的自动执行。
    比如,有一个 Generator 函数,用于依次读取两个文件。
var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};

co 函数库可以让你不用编写 Generator 函数的执行器。

var co = require('co');
co(gen);

上面代码中,Generator 函数只要传入 co 函数,就会自动执行。

  • co 函数库的原理
    Generator 函数就是一个异步操作的容器。它的自动执行需要一种机制,当异步操作有了结果,能够自动交回执行权。

两种方法可以做到这一点。
(1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。
(2)Promise 对象。将异步操作包装成 Promise 对象,用 then 方法交回执行权。

co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。

  • co 函数库的源码
    function co(gen){
        var ctx = this
        return new Promise(function(resolve,reject){
            if(typeof gen === 'function') gen = gen.call(ctx)
            if(!gen || typeof gen.next !== 'function') return resolve(gen)
            onFulfilled()
            function onFulfilled(res){
                var ret
                try{
                    ret = gen.next(res)
                }catch(e){
                    retrun 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 支持并发的异步操作,即允许某些操作同时进行,等到它们全部完成,才进行下一步。
    这时,要把并发的操作都放在数组或对象里面。

// 数组的写法
co(function* () {
  var res = yield [
    Promise.resolve(1),
    Promise.resolve(2)
  ];
  console.log(res); 
}).catch(onerror);

// 对象的写法
co(function* () {
  var res = yield {
    1: Promise.resolve(1),
    2: Promise.resolve(2),
  };
  console.log(res); 
}).catch(onerror);

async 函数

  • async 函数含义
    async 函数就是 Generator 函数的语法糖。
var asyncReadFile = async function (){
  var f1 = await readFile('/etc/fstab');
  var f2 = await readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
  • async 函数的优点
  1. 内置执行器。 Generator 函数的执行必须靠执行器,所以才有了 co 函数库,而 async 函数自带执行器。也就是说,async 函数的执行,与普通函数一模一样,只要一行。
var result = asyncReadFile();
  1. 更好的语义。 async 和 await,比起星号和 yield,语义更清楚了。async 表示函数里有异步操作,await 表示紧跟在后面的表达式需要等待结果。
  2. 更广的适用性。 co 函数库约定,yield 命令后面只能是 Thunk 函数或 Promise 对象,而 async 函数的 await 命令后面,可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
  • async 函数的实现
    async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里。

async function fn(args){
  // ...
}

// 等同于

function fn(args){ 
  return spawn(function*() {
    //spawn 函数为自动执行器 ...
  }); 
}
  • async 函数的用法
    同 Generator 函数一样,async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
  • 注意点
  1. await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try...catch 代码块中。
  2. await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。
  3. 如果确实希望多个请求并发执行,可以使用 Promise.all 方法。

相关文章

  • JavaScript异步编程好文摘要

    JavaScript之异步编程简述JavaScript异步编程

  • part1整理

    函数式编程:JavaScript函数式编程指南 异步编程:异步编程 Promise源码 JavaScript基础知...

  • 一篇看完JS异步编程的进阶史

    一、Javascript实现异步编程的过程以及原理 1、为什么要用Javascript异步编程 众所周知,Java...

  • ES6 之 Promise

    Promise是JavaScript异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步...

  • JavaScript(ES6) - Async

    异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...

  • 异步编程控制方法

    javascript 具有的一个特性就是异步编程。异步编程具有的优势本文不做细说,本文主要是总结如何异步编程中出现...

  • Javascript------异步编程的4种方法

    Javascript异步编程的4种方法

  • 深入了解下Promise

    Promise 意义 Promise 的诞生与 Javascript 中异步编程息息相关,js 中异步编程主要指 ...

  • JavaScript学习笔记(5) 异步-- Promise

    写在前面 异步编程对Javascript语言非常重要,在Javascript的发展道路上,异步编程的方法也是一直在...

  • AJAX入门

    AJAX(Async Javascript and Xml):在AJAX中的异步不是异步编程中的异步,而是泛指“局...

网友评论

      本文标题:JavaScript异步编程

      本文链接:https://www.haomeiwen.com/subject/yfvwpctx.html