理解co执行逻辑

对于co的一个最简版的实现,代码如下 (https://gist.github.com/iwillwen/8488050)

function co(generator) {
  return function(fn) {
    var gen = generator();
    function next(err, result) {
        if(err){
            return fn(err);
        }
        var step = gen.next(result);
        if (!step.done) {
            step.value(next);
        } else {
            fn(null, step.value);
        }
    }
    next();
   }
}

为了理解这个代码,来看一个例子

var co = require(‘./co‘);
// wrap the function to thunk
function readFile(filename) {
    return function(callback) {
        require(‘fs‘).readFile(filename, ‘utf8‘, callback);
    };
}

co(function * () {
    var file1 = yield readFile(‘./file/a.txt‘);
    var file2 = yield readFile(‘./file/b.txt‘);

    console.log(file1);
    console.log(file2);
    return ‘done‘;
})(function(err, result) {
    console.log(result)
});

现在就执行顺序来说明一下

  1. 首先是以下部分
 co(function * () {
        var file1 = yield readFile(‘./file/a.txt‘);
        var file2 = yield readFile(‘./file/b.txt‘);

        console.log(file1);
        console.log(file2);
        return ‘done‘;
    })

   这部分代码执行了co函数,并把function*()作为参数generator传入,也就是说,整个部分变成了

function(fn) {
    var gen = generator();
    function next(err, result) {
        if(err){
            return fn(err);
        }
        var step = gen.next(result);
        if (!step.done) {
            step.value(next);
        } else {
            fn(null, step.value);
        }
    }
    next();
   }(function(err, result) {
    console.log(result)
})

  这个部分执行这个function,并把

function(err, result) {  console.log(result)
}

  作为fn的实参传入。

  2. 执行过程中,`var gen`为 `function *()`部分,下面是函数`next()`的声明,之后执行了`next()`函数,此时的`err`和`result`均为空。

  3. `err`为空,因此`if(err)`不执行,执行 `var step = gen.next(result);`,由于`result`这里为空,因此

var step = gen.next(result)
step.value
=readFile(‘./file/a.txt‘)
=function(callback) {
        require(‘fs‘).readFile(‘./file/a.txt‘, ‘utf8‘, callback);
   };

  4. `step.done `为`false`, 执行`step.value(next);`,即执行

function(callback) {
    require(‘fs‘).readFile(‘./file/a.txt‘, ‘utf8‘, callback);
};

  函数,并`next`作为`callback`传入。`readFile`传入回调函数的参数为`err`和`data`,因此在读取完文件后,`err`和a文件的内容(作为result的实参)传入了 `next`函数并执行。

  5. 与2,3类似,执行`var step = gen.next(result)`,这一步由于result的值为a文件的内容,因此`file1`被赋值为a文件的内容(next方法可以带一个参数,该参数就会被当作上一个yield语句的返回值。)。与4类似,err和b文件的内容(作为result的实参)传入`next`函数并执行。

  6. 再次执行到`var step = gen.next(result)`的时候,`file2`被赋予文件b的内容,由于`generator`下面没有`yield`了,于是执行到`return`,`step.value=‘done‘`,`step.done=true`,函数从上次yield语句停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)。(next方法返回的对象的value属性,就是紧跟在return语句后面的表达式的值(如果没有return语句,则value属性的值为undefined),done属性的值true,表示遍历已经结束。)

  7. 执行`fn(null, step.value);` 即

function(err, ‘done‘) {
    console.log(‘done‘);
}();

  8. 因此代码的执行结果为

  content in a.txt

  content in b.txt

  done

时间: 2024-08-22 04:12:35

理解co执行逻辑的相关文章

记事本写c#代码编译并理解编译执行原理

1.在记事本里编写c#代码,将文件保存为ProgramTest.cs: namespace huangxiangTestDemo { class ProgramTest { static void Main() { System.Console.WriteLine("hello world"); System.Console.ReadKey(); } } } 2.在控制台编译,使用csc命令编译: 注意完整写法:csc.exe /out:ProgramTest.exe /t:exe /

discuz 3.2 discuz_application.php代码执行逻辑顺序分析

discuz 3.2 discuz_application.php代码执行逻辑顺序分析 说明 步骤1.   discuz_application文件中定义了_init_env(),在此方法中定义了超级全局变量$_G(global $_G) 步骤2.   discuz_application的构造函数中初始化了_init_env()方法 步骤3.   实例化discuz_application 步骤4.   在forum_index.php文件中使用,discuz_application中定义的方

GO中DEFER的理解--DEFER执行的原理

在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用.因此,defer通常用来释放函数内部变量. 为了更好的学习defer的行为,我们首先来看下面一段代码: func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { re

Android学习路线(二十五)全面理解Android Navigation逻辑

应用导航的一致性是整体用户体验的重要组成部分,如果app的导航方式不一样,用户不能很快理解,那么这个应用会让用户有很大的挫败感,大大地影响了用户体验. Android 3.0后,系统像大家介绍了其在全局导航表现上的重大改变.全面地理解“Back”以及“Up”的导航效果以及意义,能够大大地减少用户的学习时间,用户在使用过程中很快能够学习如何在应用的各个界面间的切换. Android 2.3以及它之前的系统都是通过“Back”按钮来为app导航的.在Android 3.0后出现的Actionbar则

深入理解javascript执行上下文(Execution Context)

本文转自:http://blogread.cn/it/article/6178 在这篇文章中,将比较深入地阐述下执行上下文 - Javascript中最基础也是最重要的一个概念.相信读完这篇文章后,你就会明白javascript引擎内部在执行代码以前到底做了些什么,为什么某些函数以及变量在没有被声明以前就可以被使用,以及它们的最终的值是怎样被定义的. 什么是执行上下文 Javascript中代码的运行环境分为以下三种: 全局级别的代码 - 这个是默认的代码运行环境,一旦代码被载入,引擎最先进入的

理解 Javascript 执行上下文和执行栈

如果你是一名 JavaScript 开发者,或者想要成为一名 JavaScript 开发者,那么你必须知道 JavaScript 程序内部的执行机制.理解执行上下文和执行栈同样有助于理解其他的 JavaScript 概念如提升机制.作用域和闭包等. 正确理解执行上下文和执行栈的概念将有助于你成为一名更好的 JavaScript 开发人员. 废话不多说,让我们切入正题. 什么是执行上下文 简而言之,执行上下文就是当前 JavaScript 代码被解析和执行时所在环境的抽象概念, JavaScrip

Nuint-测试执行逻辑

Nuint- using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using NUnit.Framework; namespace NC_Test._002Nc_Alac { [TestFixture] public class Alac_Test01 { String sIP = "192.168.1.1"; [TestMethod] public void TestMethod1() { Console

处理器(也就是执行流程图上面的controller)执行逻辑(十)

处理器继承自:AbstractController,MultiActionController   一.AbstractController 如上图,可以分析出AbstractController用的处理器适配器是SimpleControllerHandlerAdapter   具体Mycontroller里面的handleRequestInternal这个方法是怎么执行的,上图里面AbstractController类是继承了Controller接口,Controller这个接口里面有一个如下

PHP_输出缓存(output_buffering)的深入理解,边执行边输出

首先明确一下PHP的输出顺序1.打开了php输出缓存: echo,print -> php output_buffring -> server buffering -> browser buffering -> browser display2.未打开php输出缓存: echo,print -> server buffering -> browser buffering -> browser display 另外明确一下浏览器的输出缓存:IE为256Bytes,