NanUI文书档案目录

后边的话

用循环语句迭代数据时,必供给起头化1个变量来记录每三遍迭代在数码集合中的地方,而在见怪不怪编制程序语言中,已经初步通进度序化的法子用迭代器对象回来迭代进度中聚集的每2个要素

何以贯彻C#与Javascript的相互通信

透过事先的小说,相信您曾经对NanUI有了启幕的问询。但到如今停止,我们利用NanUI仅仅只是作为展现HTML界面包车型地铁容器,并未涉嫌CEF与C#间数据的相互。那么本文将不难介绍怎么着在NanUI中使用C#调用Javascript的函数以及哪些在Javascript注入C#的指标、属性和办法。

迭代器的施用可以极大地简化数据操作,于是ES6也向JS中添加了这几个迭代器特性。新的数组方法和新的成团类型(如Set集合与Map集合)都依靠迭代器的完成,那个新特征对于神速的多寡处理而言是必备的,在言语的任何特色中也都有迭代器的身影:新的for-of循环、展开运算符(…),甚至连异步编制程序都足以运用迭代器

C#调用Javascript函数

本文将详细介绍ES6中的迭代器(Iterator)和生成器(Generator)

不须要取得重回值的情况

要是页面中犹如下Javascript的函数sayHello,它的效能是在DOM中开创叁个涵盖有“Hello
NanUI!”字样的p元素。

function sayHello() {
    var p = document.createElement("p");
    p.innerText = "Hello NanUI!";

    var container = document.getElementById("hello-container");
    container.appendChild(p);
}

演示中,该函数并从未在Javascript环境里调用,而是在页面加载成功后选用NanUI的ExecuteJavascript措施来调用它。ExecuteJavascript方法执行的归来结果为一个bool类型,它提醒了这次有未遂执行。

在窗体的构造函数中,通过挂号Formium的LoadHandler中的OnLoadEnd事件来监测页面加载成功的情景,并在页面加载成功后调用JS环境中的函数sayHello。

namespace CommunicateBetweenJsAndCSharp
{
    using NetDimension.NanUI;
    public partial class Form1 : Formium
    {
        public Form1()
            : base("http://res.app.local/www/index.html",false)
        {
            InitializeComponent();

            LoadHandler.OnLoadEnd += LoadHandler_OnLoadEnd;
        }

        private void LoadHandler_OnLoadEnd(object sender, Chromium.Event.CfxOnLoadEndEventArgs e)
        {
            // Check if it is the main frame when page has loaded.
            if(e.Frame.IsMain)
            {
                ExecuteJavascript("sayHello()");
            }
        }
    }
}

运转后,能够见见界面中突显了“Hello
NanUI!”字样,表明使用ExecuteJavascript可以调用JS函数。


引入

亟需获得重返值的事态

上边的事例中通过ExecuteJavascript主意来成功调用了JS环境中的函数。但简单发现,那种调用方式C#是从未有过吸收到其余重回值的。但实际上的连串里,我们是亟需从JS环境获得到重回值的,这时候使用ExecuteJavascript将不可能满足必要,使用其它1个主意EvaluateJavascript能够扶持我们从JS环境中获得JS函数的再次回到值。

假若有其它二个Javascript函数sayHelloToSomeone,它能接过一个字符传参数,在函数体中拼接并赶回拼接后的字符串。

function sayHelloToSomeone(who) {
    return "Hello " + who + "!";
}

同一的,在上头例子LoadHandler的OnLoadEnd事件中大家来实施sayHelloToSomeone,并通过C#传递参数并取得拼接后的重回值。EvaluateJavascript主意通过3个回调Action来获取JS环境中的再次回到值。那几个Action有八个参数,第3个是重临值的聚合,第1个是JS环境的不得了对象,假若函数正确履行,那么第③个参数为null

namespace CommunicateBetweenJsAndCSharp
{
    using NetDimension.NanUI;
    public partial class Form1 : Formium
    {
        public Form1()
            : base("http://res.app.local/www/index.html",false)
        {
            InitializeComponent();

            LoadHandler.OnLoadEnd += LoadHandler_OnLoadEnd;
        }

        private void LoadHandler_OnLoadEnd(object sender, Chromium.Event.CfxOnLoadEndEventArgs e)
        {
            // Check if it is the main frame when page has loaded.
            if(e.Frame.IsMain)
            {
                EvaluateJavascript("sayHelloToSomeone('C#')", (value, exception) =>
                {
                    if(value.IsString)
                    {
                        // Get value from Javascript.
                        var jsValue = value.StringValue;

                        MessageBox.Show(jsValue);
                    }
                });
            }
        }
    }
}

在上头的示范中,通过大家得以一目通晓知道JS函数sayHelloToSomeone的再次回到值一定为String类型,因而在C#的回调中一向动用Value的StringValue属性来博取JS函数的字符传重回值。但在其实的运用中,有也许并不完全明了再次回到值的项目,由此要求采纳Value中放置的逐条判断属性来挨家挨户筛选再次回到值。

亟待专注的是,Value的类是是ChromiumFX中的CfrV8Value项目,它是四个很是重要的项目,基本上全部在C#与CEF间的通讯都以由那么些项目来形成的。


上边是一段正式的for循环代码,通过变量i来跟踪colors数组的目录,循环每回执行时,若是i小于数老板度len则加1,并履行下2回循环

Javascript调用C#对象及格局

var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
 console.log(colors[i]);
}

不难的施用示范

上边的稿子中示范了怎样用C#来调用Javascript中的函数,那么下边包车型客车内容将介绍如何利用Javascript来调用C#中的对象、属性和各类方式。

以前,须求介绍NanUI窗体基类Formium中的首要性质GlobalObject,您能够把她领略成Javascript环境中的window目的。要是您须求在Javascript环境下使用C#中的种种对象、属性和办法,都亟待将这么些指标、属性、方法注册到GlobalObject里。

下边包车型客车例子,通过在Form1的构造函数中注册三个名为my的JS对象,并在my松手三个只读属性name,以及showCSharpMessageBoxgetArrayFromCSharpgetObjectFromCSharp多个函数。

//register the "my" object
var myObject = GlobalObject.AddObject("my");

//add property "name" to my, you should implemnt the getter/setter of name property by using PropertyGet/PropertySet events.
var nameProp = myObject.AddDynamicProperty("name");
nameProp.PropertyGet += (prop, args) =>
{
    // getter - if js code "my.name" executes, it'll get the string "NanUI". 
    args.Retval = CfrV8Value.CreateString("NanUI");
    args.SetReturnValue(true);
};
nameProp.PropertySet += (prop, args) =>
{
    // setter's value from js context, here we do nothing, so it will store or igrone by your mind.
    var value = args.Value;
    args.SetReturnValue(true);
};


//add a function showCSharpMessageBox
var showMessageBoxFunc = myObject.AddFunction("showCSharpMessageBox");
showMessageBoxFunc.Execute += (func, args) =>
{
    //it will be raised by js code "my.showCSharpMessageBox(`some text`)" executed.
    //get the first string argument in Arguments, it pass by js function.
    var stringArgument = args.Arguments.FirstOrDefault(p => p.IsString);

    if (stringArgument != null)
    {
        MessageBox.Show(this, stringArgument.StringValue, "C# Messagebox", MessageBoxButtons.OK, MessageBoxIcon.Information);


    }
};

//add a function getArrayFromCSharp, this function has an argument, it will combind C# string array with js array and return to js context.
var friends = new string[] { "Mr.JSON", "Mr.Lee", "Mr.BONG" };

var getArrayFromCSFunc = myObject.AddFunction("getArrayFromCSharp");

getArrayFromCSFunc.Execute += (func, args) =>
{
    var jsArray = args.Arguments.FirstOrDefault(p => p.IsArray);



    if (jsArray == null)
    {
        jsArray = CfrV8Value.CreateArray(friends.Length);
        for (int i = 0; i < friends.Length; i++)
        {
            jsArray.SetValue(i, CfrV8Value.CreateString(friends[i]));
        }
    }
    else
    {
        var newArray = CfrV8Value.CreateArray(jsArray.ArrayLength + friends.Length);

        for (int i = 0; i < jsArray.ArrayLength; i++)
        {
            newArray.SetValue(i, jsArray.GetValue(i));
        }

        var jsArrayLength = jsArray.ArrayLength;

        for (int i = 0; i < friends.Length; i++)
        {
            newArray.SetValue(i + jsArrayLength, CfrV8Value.CreateString(friends[i]));
        }


        jsArray = newArray;
    }


    //return the array to js context

    args.SetReturnValue(jsArray);

    //in js context, use code "my.getArrayFromCSharp()" will get an array like ["Mr.JSON", "Mr.Lee", "Mr.BONG"]
};

//add a function getObjectFromCSharp, this function has no arguments, but it will return a Object to js context.
var getObjectFormCSFunc = myObject.AddFunction("getObjectFromCSharp");
getObjectFormCSFunc.Execute += (func, args) =>
{
    //create the CfrV8Value object and the accssor of this Object.
    var jsObjectAccessor = new CfrV8Accessor();
    var jsObject = CfrV8Value.CreateObject(jsObjectAccessor);

    //create a CfrV8Value array
    var jsArray = CfrV8Value.CreateArray(friends.Length);

    for (int i = 0; i < friends.Length; i++)
    {
        jsArray.SetValue(i, CfrV8Value.CreateString(friends[i]));
    }

    jsObject.SetValue("libName", CfrV8Value.CreateString("NanUI"), CfxV8PropertyAttribute.ReadOnly);
    jsObject.SetValue("friends", jsArray, CfxV8PropertyAttribute.DontDelete);


    args.SetReturnValue(jsObject);

    //in js context, use code "my.getObjectFromCSharp()" will get an object like { friends:["Mr.JSON", "Mr.Lee", "Mr.BONG"], libName:"NanUI" }
};

运文章种开辟CEF的DevTools窗口,在Console中输入my,就能收看my指标的详细消息。

365bet体育投注 1

执行my.showCSharpMessageBox(“SOME TEXT FROM
JS”)
命令,将调用C#的MessageBox来具体JS函数中提供的“SOME TEXT FROM
JS”字样。

执行my.getArrayFromCSharp()能够从C#中取到大家松手的字符串数组中的多少个字符串。假设在函数中内定了二个数组作为参数,那么钦点的这几个数组将和C#的字符串数组合并。

> my.getArrayFromCSharp()
["Mr.JSON", "Mr.Lee", "Mr.BONG"]

> my.getArrayFromCSharp(["Js_Bison", "Js_Dick"])
["Js_Bison", "Js_Dick", "Mr.JSON", "Mr.Lee", "Mr.BONG"]

执行my.getObjectFromCSharp()能够从C#回来大家拼装的目的,该对象有1个字符型的libName天性,以及二个字符串数组friends

> my.getObjectFromCSharp()
Object {libName: "NanUI", friends: Array(3)}

即使循环语句语法不难,但假使将五个巡回嵌套则供给追踪多少个变量,代码复杂度会大大扩充,一相当大心就漏洞百出选拔了别的for循环的跟踪变量,从而造成程序出错。迭代器的出现目的在于消除这种复杂并缩减循环中的错误

回调函数

回调函数是Javascript里面重要和常用的效果,如若您在JS环境中注册的法门具有函数型的参数(即回调函数),通过Execute事件的Arguments能够博得回调的function,并选拔CfrV8Value的ExecuteFunction来执行回调。

//add a function with callback function

var callbackTestFunc = GlobalObject.AddFunction("callbackTest");
callbackTestFunc.Execute += (func,args)=> {
    var callback = args.Arguments.FirstOrDefault(p => p.IsFunction);
    if(callback != null)
    {
        var callbackArgs = CfrV8Value.CreateObject(new CfrV8Accessor());
        callbackArgs.SetValue("success", CfrV8Value.CreateBool(true), CfxV8PropertyAttribute.ReadOnly);
        callbackArgs.SetValue("text", CfrV8Value.CreateString("Message from C#"), CfxV8PropertyAttribute.ReadOnly);

        callback.ExecuteFunction(null, new CfrV8Value[] { callbackArgs });
    }
};

在Console中执行callbackTest(function(result){ console.log(result);
})
将实施匿名回调,并得到到C#回传的result对象。

> callbackTest(function(result){ console.log(result); })
Object {success: true, text: "Message from C#"}

在大部处境下,在Javascript中回调都以因为执行了一部分异步的操作,那么只要这么些异步的操作是在C#执行也是实用的,只是完毕起来就相比较复杂。上边将演示如何促成八个异步回调。

//add a function with async callback
var asyncCallbackTestFunc = GlobalObject.AddFunction("asyncCallbackTest");
asyncCallbackTestFunc.Execute += async (func, args) => {
//save current context
var v8Context = CfrV8Context.GetCurrentContext();
var callback = args.Arguments.FirstOrDefault(p => p.IsFunction);

//simulate async methods.
await Task.Delay(5000);

if (callback != null)
{
    //get render process context
    var rc = callback.CreateRemoteCallContext();

    //enter render process
    rc.Enter();

    //create render task
    var task = new CfrTask();
    task.Execute += (_, taskArgs) =>
    {
        //enter saved context
        v8Context.Enter();

        //create callback argument
        var callbackArgs = CfrV8Value.CreateObject(new CfrV8Accessor());
        callbackArgs.SetValue("success", CfrV8Value.CreateBool(true), CfxV8PropertyAttribute.ReadOnly);
        callbackArgs.SetValue("text", CfrV8Value.CreateString("Message from C#"), CfxV8PropertyAttribute.ReadOnly);

        //execute callback
        callback.ExecuteFunction(null, new CfrV8Value[] { callbackArgs });


        v8Context.Exit();

        //lock task from gc
        lock (task)
        {
            Monitor.PulseAll(task);
        }
    };

    lock (task)
    {
        //post task to render process
        v8Context.TaskRunner.PostTask(task);
    }

    rc.Exit();

    GC.KeepAlive(task);
}

在Console中执行asyncCallbackTest(function(result){
console.log(result);
})
将推行匿名回调,大概5秒后获得到C#回传的result对象。

> asyncCallbackTest(function(result){ console.log(result); })
Object {success: true, text: "Message from C#"}

以上,您已经不难询问了运用NanUI如何是好到C#和Javascript的并行通信。NanUI基于开源项目ChromiumFX开发,由此C#与Javascript的互相与ChomiumFX保持一致,假若急需付出越发扑朔迷离的功力,请自行检索和参照ChromiumFX的连带API及示范。

迭代器

示范源码

git clone https://github.com/NetDimension/NanUI-Examples-04-Communicate-Between-CSharp-And-JS.git

迭代器是一种格外目标,它具有局地尤其为迭代经过规划的专有接口,全体的迭代器对象都有3个next()方法,每便调用都回到一个结果对象。结果对象有几个天性:一个是value,表示下3个快要再次来到的值;另三个是done,它是叁个布尔类型的值,当没有越来越多可回到数据时回来true。迭代器还会保留贰个内部指针,用来针对当前集结中值的地方,每调用二次next()方法,都会回去下一个可用的值

365bet体育投注,社会群众体育和扶植

GitHub
https://github.com/NetDimension/NanUI/

交流群QQ群
521854872

支持我

若是你喜欢本人的劳作,并且期望NanUI持续的上进,请对NanUI项目进展协理以此来鼓励和匡助笔者一连NanUI的支出工作。你能够使用微信或者支付宝来围观上边包车型地铁二维码举行帮助。

365bet体育投注 2

固然在尾数值再次来到后再调用next()方法,那么重返的对象中性能done的值为true,属性value则含有迭代器最后回到的值,那几个重返值不是数据集的一有的,它与函数的重返值类似,是函数调用进程中最终三遍给调用者传递音讯的主意,要是没有相关数据则重临undefined

上边用ES5的语法创设2个迭代器

function createIterator(items) {
 var i = 0;
 return {
  next: function() {
   var done = (i >= items.length);
   var value = !done ? items[i++] : undefined;
   return {
    done: done,
    value: value
   };
  }
 };
}
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

在地方那段代码中,createIterator()方法再次回到的对象有三个next()方法,每一遍调用时,items数组的下3个值会作为value重临。当i为3时,done变为true;此时岁旦表明式会将value的值设置为undefined。最终四次调用的结果与ES6迭代器的最后回到机制仿佛,当数码集被用尽后会重返最后的内容

上边这一个示例很复杂,而在ES6中,迭代器的编写规则也同等复杂,但ES6并且还引入了三个生成器对象,它能够让成立迭代器对象的长河变得更简单

生成器

生成器是一种回到迭代器的函数,通过function关键字后的星号(*)来代表,函数中会用到新的根本字yield。星号能够紧挨着function关键字,也足以在中游添加1个空格

// 生成器
function *createIterator() {
 yield 1;
 yield 2;
 yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

在那个示例中,createlterator()前的星号注脚它是贰个生成器;yield关键字也是ES6的新特征,能够经过它来钦赐调用迭代器的next()方法时的再次来到值及重回顺序。生成迭代器后,延续三次调用它的next()方法再次回到叁个不等的值,分别是壹 、2和3。生成器的调用进度与别的函数一样,最后回到的是成立好的迭代器

生成器函数最有趣的一部分是,每当执行完一条yield语句后函数就会自动截至执行。举个例子,在上头那段代码中,执行完语句yield
1之后,函数便不再实行其它任何语句,直到再也调用迭代器的next()方法才会三番五次执行yield
2语句。生成器函数的那种中止函数执行的力量有好多有趣的选择

使用yield关键字能够回来任何值或表明式,所以能够经过生成器函数批量地给迭代器添日币素。例如,能够在循环中选择yield关键字

function *createIterator(items) {
 for (let i = 0; i < items.length; i++) {
  yield items[i];
 }
}
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

在此示例中,给生成器函数createlterator()传入一个items数组,而在函数内部,for循环不断从数组中生成新的要素放入迭代器中,每境遇多个yield语句循环都会告一段落;每一遍调用迭代器的next()方法,循环会继续运维并施行下一条yield语句

生成器函数是ES6中的一个主要特色,能够将其用来全数扶助函数使用的地点

【使用限制】

yield关键字只可在生成器内部使用,在别的市方选拔会造成程序抛出荒唐

function *createIterator(items) {
 items.forEach(function(item) {
  // 语法错误
  yield item + 1;
 });
}

从字面上看,yield关键字实在在createlterator()函数内部,可是它与return关键字一样,二者都无法穿透函数边界。嵌套函数中的return语句不可能同日而语外部函数的回到语句,而那里嵌套函数中的yield语句会导致程序抛出语法错误

【生成器函数表明式】

也可以透过函数表明式来创制生成器,只需在function关键字和小括号中间添加三个星号(*)即可

let createIterator = function *(items) {
 for (let i = 0; i < items.length; i++) {
  yield items[i];
 }
};
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

在那段代码中,createlterator()是二个生成器函数表达式,而不是3个函数注解。由于函数表达式是匿名的,由此星号直接放在function关键字和小括号之间。此外,那个示例基本与前例相同,使用的也是for循环

[注意]不能用箭头函数来成立生成器

【生成器对象的主意】

出于生成器自身正是函数,由此可以将它们增加到目的中。例如,在ES5品格的指标字面量中,能够通过函数表明式来创建生成器

var o = {
 createIterator: function *(items) {
   for (let i = 0; i < items.length; i++) {
    yield items[i];
   }
  }
};
let iterator = o.createIterator([1, 2, 3]);

也足以用ES6的函数方法的简写情势来创立生成器,只需在函数名前添加三个星号(*)

var o = {
 *createIterator(items) {
   for (let i = 0; i < items.length; i++) {
    yield items[i];
   }
  }
};
let iterator = o.createIterator([1, 2, 3]);

那个示例使用了不一致于之前的语法,但它们的效率实在是等价的。在简写版本中,由于不采用function关键字来定义createlterator()方法,由此就算能够在星号和方法名以内留白,但要么将星号紧贴在方法名在此以前

【状态机】

生成器的3个常用功效是生成状态机

let state = function*(){
 while(1){
  yield 'A';
  yield 'B';
  yield 'C';
 }
}

let status = state();
console.log(status.next().value);//'A'
console.log(status.next().value);//'B'
console.log(status.next().value);//'C'
console.log(status.next().value);//'A'
console.log(status.next().value);//'B'

可迭代对象

可迭代对象具备Symbol.iterator属性,是一种与迭代器密切相关的对象。Symbol.iterator通过点名的函数能够回到三个功能于附属对象的迭代器。在ES6中,全体的集聚对象(数组、Set集合及Map集合)和字符串都以可迭代对象,这一个指标中都有私下认可的迭代器。ES6中新参与的表征for-of循环要求用到可迭代对象的这几个作用

[注意]由于生成器暗中认可会为Symbol.iterator属性赋值,由此具有通过生成器创立的迭代器都以可迭代对象

一初始,大家曾涉嫌过巡回之中索引跟踪的连带难点,要缓解那几个题材,供给四个工具:一个是迭代器,另一个是for-of循环。如此一来,便不须要再跟踪整个集合的目录,只需关心集合中要处理的内容

for-of循环每执行3次都会调用可迭代对象的next()方法,并将迭代器再次来到的结果对象的value属性存储在三个变量中,循环将随处实施这一进程直到回到对象的done属性的值为true。这里有个示范

let values = [1, 2, 3];
for (let num of values) {
 //1
 //2
 //3
 console.log(num);
}

那段for-of循环的代码通过调用values数组的Symbol.iterator方法来赢得迭代器,这一进程是在JS引擎背后实现的。随后迭代器的next()方法被频仍调用,从其回来对象的value属性读取值并蕴藏在变量num中,依次为1、2和3,当结果对象的done属性值为true时轮回退出,所以num不会被赋值为undefined

假定只需迭代数组或集合中的值,用for-of循环代替for循环是个不利的选料。相比较传统的for循环,for-of循环的主宰标准更简便,不须要追踪复杂的基准,所以更少出错

[注意]一经将for-of语句用于不可迭代对象、null或undefined将会造成程序抛出错误

【访问默许迭代器】

能够因此Symbol.iterator来访问对象暗许的迭代器

let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

在那段代码中,通过Symbol.iterator获取了数组values的暗中认可迭代器,并用它遍历数组中的成分。在JS引擎中施行for-of循环语句时也会有类似的处理进程

鉴于有着Symbol.iterator属性的靶子都有暗许的迭代器,由此能够用它来检查和测试对象是不是为可迭代对象

function isIterable(object) {
 return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new WeakMap())); // false
console.log(isIterable(new WeakSet())); // false

此间的islterable()函数可以检查内定对象中是否存在默许的函数类型迭代器,而for-of循环在推行前也会做一般的反省

而外接纳内建的可迭代对象类型的Symbol.iterator,也足以应用Symbol.iterator来创立属于本人的迭代器

【创造可迭代对象】

私下认可情状下,开发者定义的对象都以不可迭代对象,但假诺给Symbol.iterator属性添加1个生成器,则足以将其变成可迭代对象

let collection = {
 items: [],
 *[Symbol.iterator]() {
  for (let item of this.items) {
   yield item;
  }
 }
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
 //1
 //2
 //3
 console.log(x);
}

在那么些示例中,先创建三个生成器(注意,星号仍旧在性质名前)并将其赋值给指标的Symbol.iterator属性来创立私下认可的迭代器;而在生成器中,通过for-of循环迭代this.items并用yield重回每三个值。collection对象默许迭代器的回到值由迭代器this.items自动生成,而非手动遍历来定义重返值

【展开运算符和非数组可迭代对象】

经过开始展览运算符(…)可以把Set集合转换成二个数组

let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1,2,3,4,5]

那段代码中的展开运算符把Set集合的保有值填充到了三个数组字面量里,它能够操作全体可迭代对象,并基于暗中认可迭代器来摘取要引用的值,从迭代器读取全数值。然后根据重临顺序将它们依次插入到数组中。Set集合是三个可迭代对象,展开运算符也能够用于其余可迭代对象

let map = new Map([ ["name", "huochai"], ["age", 25]]),
array = [...map];
console.log(array); // [ ["name", "huochai"], ["age", 25]]

进展运算符把Map集合转换来包括三个数组的数组,Map集合的私下认可迭代器再次回到的是多组键值对,所以结果数组与执行new
Map()时传出的数组看起来一样

在数组字面量中得以频仍应用举办运算符,将可迭代对象中的多少个因素依次插入新数组中,替换原先展开运算符所在的岗位

let smallNumbers = [1, 2, 3],
bigNumbers = [100, 101, 102],
allNumbers = [0, ...smallNumbers, ...bigNumbers];
console.log(allNumbers.length); // 7
console.log(allNumbers); // [0, 1, 2, 3, 100, 101, 102]

开创贰个变量allNumbers,用展开运算符将smallNumbers和bigNumbers里的值依次添加到allNumbers中。首先存入0,然后存入small中的值,最终存入bigNumbers中的值。当然,原始数组中的值只是被复制到allNumbers中,它们本人并未改观

由于开始展览运算符能够功能于自由可迭代对象,由此只要想将可迭代对象转换为数组,那是最简单易行的法子。既能够将字符串中的每3个字符(不是编码单元)存入新数组中,也足以将浏览器中NodeList对象中的每叁个节点存入新的数组中

内建迭代器

迭代器是ES6的1个生死攸关组成部分,在ES6中,已经暗中认可为许多内建品种提供了内建迭代器,唯有当那个内建迭代器无法达成目的时才须要团结创造。日常来说当定义本人的靶子和类时才会遇上那种气象,否则,完全能够借助内建的迭代器达成工作,而最常使用的或者是汇集的那个迭代器

【集合对象迭代器】

在ES6中有3种类型的汇集对象:数组、Map集合与Set集合

为了更好地拜会对象中的内容,这3种对象都内建了以下两种迭代器

entries() 重返2个迭代器,其值为多少个键值对
values() 重回一个迭代器,其值为汇集的值
keys() 重临三个迭代器,其值为集聚中的全数键名

调用以上三个艺术都得以访问集合的迭代器

entries()迭代器

历次调用next()方法时,entries()迭代器都会回去多少个数组,数组中的三个成分分别代表集合中各样成分的键与值。如若被遍历的指标是数组,则第三个成分是数字类型的目录;如若是Set集合,则率先个因素与首个要素都是值(Set集合中的值被同时作为键与值使用);假诺是Map集合,则第三个因素为键名

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let entry of colors.entries()) {
 console.log(entry);
}
for (let entry of tracking.entries()) {
 console.log(entry);
}
for (let entry of data.entries()) {
 console.log(entry);
}

调用console.log()方法后输出以下内容

[0, "red"]
[1, "green"]
[2, "blue"]
[1234, 1234]
[5678, 5678]
[9012, 9012]
["title", "Understanding ES6"]
["format", "ebook"]

在那段代码中,调用每种集合的entries()方法获得三个迭代器,并动用for-of循环来遍历成分,且经过console将每八个指标的键值对输出出来

values()迭代器

调用values()迭代器时会再次回到集合中所存的全数值

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let value of colors.values()) {
 console.log(value);
}
for (let value of tracking.values()) {
 console.log(value);
}
for (let value of data.values()) {
 console.log(value);
}

调用console.log()方法后输出以下内容

"red"
"green"
"blue"
1234
5678
9012
"Understanding ES6"
"ebook"

如上所示,调用values()迭代器后,再次来到的是种种集合中蕴藏的确实数据,而不包含数据在聚集中的位置音信

keys()迭代器

keys()迭代器会回到集合中存在的每三个键。借使遍历的是数组,则会再次回到数字类型的键,数组本人的任何品质不会被再次回到;如若是Set集合,由于键与值是平等的,由此keys()和values()再次回到的也是一模一样的迭代器;假诺是Map集合,则keys()迭代器会回来种种独立的键

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "ebook");
for (let key of colors.keys()) {
 console.log(key);
}
for (let key of tracking.keys()) {
 console.log(key);
}
for (let key of data.keys()) {
 console.log(key);
}

调用console.log()方法后输出以下内容

0
1
2
1234
5678
9012
"title"
"format"

keys()迭代器会获得colors、tracking和data那3个汇集中的每二个键,而且各自在三个for-of循环之上将那几个键名打字与印刷出来。对于数组对象的话,无论是还是不是为数组添加命名属性,打字与印刷出来的都是数字类型的目录;而for-in循环迭代的是数组属性而不是数字类型的目录

今非昔比集合类型的默许迭代器

各种集合类型都有2个暗中认可的迭代器,在for-of循环中,假若没有显式钦定则使用私下认可的迭代器。数组和Set集合的暗中认可迭代器是values()方法,Map集合的暗中同意迭代器是entries()方法。有了那些暗中认可的迭代器,能够更自在地在for-of循环中运用集合对象

let colors = [ "red", "green", "blue" ];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();
data.set("title", "Understanding ES6");
data.set("format", "print");
// 与使用 colors.values() 相同
for (let value of colors) {
 console.log(value);
}
// 与使用 tracking.values() 相同
for (let num of tracking) {
 console.log(num);
}
// 与使用 data.entries() 相同
for (let entry of data) {
 console.log(entry);
}

上述代码未钦定迭代器,所以将利用暗中同意的迭代器。数组、Set集合及Map集合的私下认可迭代器也会反馈出这几个指标的开始化进度,所以那段代码会输出以下内容

"red"
"green"
"blue"
1234
5678
9012
["title", "Understanding ES6"]
["format", "print"]

私下认可景况下,假使是数组和Set集合,会挨个重临集合中存有的值。要是是Map集合,则根据Map构造函数参数的格式重返相同的数组内容。而WeakSet集合与WeakMap集合就从不内建的迭代器,由于要管理弱引用,因此不可能适用地掌握集合中留存的值,也就不能迭代这么些聚集了

【字符串迭代器】

自ES5公布之后,JS字符串稳步变得更像数组了,例如,ES5标准明确能够经过方括号访问字符串中的字符(也正是说,text[0]能够收获字符串text的首先个字符,并以此类推)。由于方括号操作的是编码单元而非字符,由此不或许正确访问双字节字符

var message = "A 𠮷 B" ;
for (let i=0; i < message.length; i++) {
 console.log(message[i]);
}

在那段代码中,访问message的length属性获取索引值,并经过方括号访问来迭代并打字与印刷二个字眼符字符串,然而出口的结果却与预期不符

A

B

出于双字节字符被作为三个独立的编码单元,从而最终在A与B之间打字与印刷出三个空行

所幸,ES6的对象是圆满协理Unicode,并且大家得以经过变更字符串的暗许迭代器来缓解那么些难题,使其操作字符而不是编码单元。今后,修改前二个示范中字符串的默许迭代器,让for-of循环输出正确的始末

var message = "A 𠮷 B" ;
for (let c of message) {
 console.log(c);
}

那段代码输出以下内容

A

𠮷

B

其一结果更契合预期,通过循环语句能够一直操作字符并成功打字与印刷出Unicode字符

【NodeList迭代器】

DOM标准中有三个NodeList类型,document对象中的全部因素都用这几个项目来表示。对于编写Web浏览器环境中的JS开发者来说,要求花点儿武功去通晓NodeList对象和数组之间的出入。二者都利用length属性来代表集合中成分的数据,都能够经过方括号来访问集合中的独立成分。而在当中贯彻中,二者的显现不行不均等,由此会导致许多麻烦

自从ES6添加了暗中同意迭代器后,DOM定义中的NodeList类型(定义在HTML标准而不是ES6正经中)也负有了暗许迭代器,其行为与数组的默许迭代器完全一致。所以能够将NodeList应用于for-of循环及其他协助对象暗中认可迭代器的地点

var divs = document.getElementsByTagName("div");
for (let div of divs) {
 console.log(div.id);
}

在那段代码中,通过调用getElementsByTagName()方法取得到document对象中享有div成分的列表,在for-of循环中遍历列表中的每三个因素并出口成分ID,实际上是根据拍卖数组的主意来处理NodeList的

高档迭代器

迭代器的底蕴成效能够支持实现很多职责,通过生成器创制迭代器的历程也很省心,除了这几个回顾的聚合遍历任务之外,迭代器也得以被用于达成都部队分繁杂的职务

【给迭代器传递参数】

迭代器既能够用迭代器的next()方法重返值,也得以在生成器内部选拔yield关键字来生成值。如若给迭代器的next()方法传递参数,则那几个参数的值就会代表生成器内部上条yield语句的重返值。而一旦要贯彻越多像异步编制程序那样的尖端作用,那么那种给迭代器传值的能力就变得至关心珍视要

function *createIterator() {
 let first = yield 1;
 let second = yield first + 2; // 4 + 2
 yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

先是次调用next()方法时无论传入什么参数都会被放弃。由于传给next()方法的参数会替代上三次yield的再次回到值,而在第1次调用next()方法前不会实施任何yield语句,由此在率先次调用next()方法时传递参数是毫无意义的

其次次调用next()方法传入数值4当做参数,它说到底被赋值给生成器函数内部的变量first。在3个含参yield语句中,表达式左侧等价于第二次调用next()方法后的下三个重回值,说明式左边等价于第贰次调用next()方法后,在函数继续执行前获得的再次来到值。第①回调用next()方法传入的值为4,它会被赋值给变量first,函数则继续执行。第①条yield语句在率先次yield的结果上加了2,最后的重回值为6

其三遍调用next()方法时,传入数值5,那一个值被赋值给second,最后用于第①条yield语句并最后回到数值8

【在迭代器中抛出错误】  

除去给迭代器传递数据外,还足以给它传递错误条件。通过throw()方法,当迭代器复苏执行时可令其抛出三个荒唐。那种积极性抛出荒谬的能力对于异步编制程序而言首要性,也能提供模拟甘休函数执行的三种方法(再次来到值或抛出错误),从而提高生成器内部的编制程序弹性。将错误对象传给throw()方法后,在迭代器继续执行时其会被抛出

function *createIterator() {
 let first = yield 1;
 let second = yield first + 2; // yield 4 + 2 ,然后抛出错误
 yield second + 3; // 永不会被执行
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // 从生成器中抛出了错误

在那么些示例中,前两个表明式寻常求值,而调用throw()方法后,在继续执行let
second求值前,错误就会被抛出并阻碍了代码继续执行。那么些进度与直接抛出荒谬很一般,二者唯一的分别是抛出的火候不比

能够在生成器内部通过try-catch代码块来捕获那几个错误

function *createIterator() {
 let first = yield 1;
 let second;
 try {
  second = yield first + 2; // yield 4 + 2 ,然后抛出错误
 } catch (ex) {
  second = 6; // 当出错时,给变量另外赋值
 }
 yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

在此示例中,try-catch代码块包裹着第2条yield语句。就算那条语句小编并未错误,但在给变量second赋值前照旧会积极抛出荒谬,catch代码块捕获错误后将second变量赋值为6,下一条yield言语继续执行后再次来到9

那边有三个幽默的场景调用throw()方法后也会像调用next()方法一致再次回到三个结实对象。由于在生成器内部捕获了这一个颠倒是非,由此会继续执行下一条yield讲话,最后回到数值9

如此一来,next()和throw()就如迭代器的两条指令,调用next()方法命令迭代器继续执行(或然提供3个值),调用throw()方法也会命令迭代器继续执行,但还要也抛出三个错误,在此之后的推行进度取决于生成器内部的代码

在迭代器内部,假使使用了yield语句,则能够因而next()方法和throw()方法控制实施进程,当然,也能够行使return语句再次来到一些与常见函数再次来到语句不太一样的始末

【生成器重回语句】

出于生成器也是函数,因而能够因此return语句提前退出函数执行,对于最终二回next()方法调用,能够积极为其钦点2个重临值。正如在其他函数中这样,能够透过return语句钦点1个重回值。而在生成器中,return代表全数操作已经完毕,属性done被安装为true;假使还要提供了对应的值,则属性value会被设置为那几个值

function *createIterator() {
 yield 1;
 return;
 yield 2;
 yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

那段代码中的生成器包涵多条yield语句和一条return语句,当中return语句紧随第二条yield语句,其后的yield语句将不会被执行

在return语句中也可以钦定三个重临值,该值将被赋值给重回对象的value属性

function *createIterator() {
 yield 1;
 return 42;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 42, done: true }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

在此示例中,第①遍调用next()方法时回来对象的value属性值为42,done属性第1遍设为true;第三遍调用next()方法仍旧重临几个对象,只是value属性的值会变为undefined。因而,通过return语句钦点的重返值,只会在回去对象中出现1回,在后续调用重临的靶子中,value属性会被重置为undefined

[注意]进展运算符与for-of循环语句会一贯忽略通过return语句钦定的其余重临值,只要done一变为true就应声结束读取别的的值。不管如何,迭代器的重回值依旧是二个11分管用的特点

【委托生成器】

在好几意况下,大家需求将多个迭代器合两为一,那时可以创造一个生成器,再给yield语句添加叁个星号,就足以将转移数据的进度委托给别的生成器。当定义这么些生成器时,只需将星号放置在关键字yield和生成器的函数名之间即可

function *createNumberIterator() {
 yield 1;
 yield 2;
}
function *createColorIterator() {
 yield "red";
 yield "green";
}
function *createCombinedIterator() {
 yield *createNumberIterator();
 yield *createColorIterator();
 yield true;
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "red", done: false }"
console.log(iterator.next()); // "{ value: "green", done: false }"
console.log(iterator.next()); // "{ value: true, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

这里的生成器createCombinedIterator()先后委托了别的五个生成器createNumberlterator()和createColorlterator()。仅依照迭代器的重回值来看,它就像2个全体的迭代器,能够变更全部的值。每贰遍调用next()方法就会委托相应的迭代器生成对应的值,直到最终由createNumberlterator()和cpeateColorlterator()创设的迭代器不能够赶回更加多的值,此时推行最后一条yield语句并赶回true

有了生成器委托那么些新职能,能够进一步应用生成器的重回值来处理丝丝缕缕职责

function *createNumberIterator() {
 yield 1;
 yield 2;
 return 3;
}
function *createRepeatingIterator(count) {
 for (let i=0; i < count; i++) {
  yield "repeat";
 }
}
function *createCombinedIterator() {
 let result = yield *createNumberIterator();
 yield *createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

在生成器createCombinedlterator()中,执行进度先被托付给了生成器createNumberlterator(),再次回到值会被赋值给变量result,执行到return
3时会回去数值3。那些值随后被传到createRepeatinglterator()作为它的参数,由此生成字符串”repeat”的yield语句会被实践3次

不论通过何种措施调用迭代器next()方法,数值3都不会被再次回到,它只设有于生成器createCombinedlterator()的内部。但要是想出口这么些值,则可以附加添加一条yield语句

function *createNumberIterator() {
 yield 1;
 yield 2;
 return 3;
}
function *createRepeatingIterator(count) {
 for (let i=0; i < count; i++) {
  yield "repeat";
 }
}
function *createCombinedIterator() {
 let result = yield *createNumberIterator();
 yield result;
 yield *createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

这边新拉长的yield语句显式地出口了生成器createNumberlterator()的重临值。

[注意]yield*也可直接运用于字符串,例如yield*
“hello”,此时将利用字符串的默许迭代器

异步任务履行

生成器令人兴奋的特征多与异步编制程序有关,JS中的异步编制程序有利有弊:不难职务的异步化格外简单;而复杂任务的异步化会推动许多管理代码的挑战。由于生成器匡助在函数中间断代码执行,由此能够浓厚发掘异步处理的更加多用法

执行异步操作的历史观方法相似是调用一个函数并施行相应回调函数

let fs = require("fs");
fs.readFile("config.json", function(err, contents) {
 if (err) {
  throw err;
 }
 doSomethingWith(contents);
 console.log("Done");
});

调用fs.readFile()方法时讲求传入要读取的公文名和八个回调函数,操作截至后会调用该回调函数并检查是或不是留存不当,假若没有就能够拍卖回来的始末。假使要执行的任务很少,那么这么的法子能够很好地成功职分;假使须求嵌套回调或类别化一文山会海的异步操作,事情会变得卓殊复杂。此时,生成器和yield语句就派上用场了

【简单职责执行器】

是因为执行yield语句会暂停当前函数的实践进度并等待下三次调用next()方法,因此得以创设3个函数,在函数中调用生成器生成相应的迭代器,从而在并非回调函数的根底上贯彻异步调用next()方法

function run(taskDef) {
 // 创建迭代器,让它在别处可用
 let task = taskDef();
 // 启动任务
 let result = task.next();
 // 递归使用函数来保持对 next() 的调用
 function step() {
  // 如果还有更多要做的
  if (!result.done) {
   result = task.next();
   step();
  }
 }
 // 开始处理过程
 step();
}

函数run()接受2个生成器函数作为参数,这一个函数定义了持续要实施的天职,生成八个迭代器并将它储存在变量task中。第三回调用迭代器的next()方法时,重临的结果被储存起来稍后继续使用。step()函数会检查result.done的值,要是为false则进行迭代器的next()方法,并再一次实施step()操作。每回调用next()方法时,重临的新型消息总会覆写变量result。在代码的末段,初叶化执行step()函数并开端全方位的迭代进度,每一回通过检查result.done来显明是不是有越多职分需求实行

依靠这几个run()函数,能够像这么实践七个带有多条yield语句的生成器

run(function*() {
 console.log(1);
 yield;
 console.log(2);
 yield;
 console.log(3);
});

本条示例最后会向决定台出口数次调用next()方法的结果,分别为数值一 、2和3。当然,不难输出迭代次数不足以彰显迭代器高级效率的实用之处,下一步将在迭代器与调用者之间相互传值

【向职分执行器传递数据】

给任务执行器传递数据的最不难易行方法是,将值通过迭代器的next()方法传入作为yield的生成值供下次调用。在那段代码中,只需将result.value传入next()方法即可

function run(taskDef) {
 // 创建迭代器,让它在别处可用
 let task = taskDef();
 // 启动任务
 let result = task.next();
 // 递归使用函数来保持对 next() 的调用
 function step() {
  // 如果还有更多要做的
  if (!result.done) {
   result = task.next(result.value);
   step();
  }
 }
 // 开始处理过程
 step();
}

近日result.value作为next()方法的参数被传到,那样就能够在yield调用之间传递数据了

run(function*() {
 let value = yield 1;
 console.log(value); // 1
 value = yield value + 3;
 console.log(value); // 4
});

此示例会向决定台出口七个数值1和4。当中,数值1取自yield
1语句中回传给变量value的值;而4取自给变量value加3后回传给value的值。今后多少已经能够在yield调用间互为传送了,只需3个小小改变便能帮忙异步调用

【异步义务执行器】

前边的演示只是在八个yield调用间来回传递静态数据,而等待3个异步进程有个别分歧。职分执行器须要驾驭回调函数是什么样以及哪些使用它。由于yield表达式会将值再次回到给职责执行器,全体的函数调用都会重返贰个值,因此在某种程度上那也是三个异步操作,任务执行器会一贯等候直到操作实现

下边定义贰个异步操作

function fetchData() {
 return function(callback) {
  callback(null, "Hi!");
 };
}

本示例的本意是让职务执行器调用的全部函数都回到二个足以实施回调进程的函数,此处fetchData()函数的重回值是2个可接受回调函数作为参数的函数,当调用它时会传入一个字符串”Hi!”作为回调函数的参数并实施。参数callback必要经过职分执行器钦命,以有限支撑回调函数执行时能够与底层迭代器正确交互。固然fetchData()是一同函数,但简单添加二个延缓方法即可将其变为异步函数

function fetchData() {
 return function(callback) {
  setTimeout(function() {
   callback(null, "Hi!");
  }, 50);
 };
}

在那一个版本的fetchData()函数中,让回调函数延迟了50ms再被调用,所以那种方式在联合和异步状态下都运维特出。只需确认保证每种要因而yield关键字调用的函数都依据与之相同的方式编写

清楚了函数中异步进程的运维方式,能够将职分执行器稍作修改。当result.value是2个函数时,职分执行器会先实施那么些函数再将结果传到next()方法

function run(taskDef) {
 // 创建迭代器,让它在别处可用
 let task = taskDef();
 // 启动任务
 let result = task.next();
 // 递归使用函数来保持对 next() 的调用
 function step() {
  // 如果还有更多要做的
  if (!result.done) {
   if (typeof result.value === "function") {
    result.value(function(err, data) {
     if (err) {
      result = task.throw(err);
      return;
     }
     result = task.next(data);
     step();
    });
   } else {
    result = task.next(result.value);
    step();
   }
  }
 }
 // 开始处理过程
 step();
}

由此===操作符检査后,倘若result.value是3个函数,会传播三个回调函数作为参数调用它,回调函数服从Node.js有关实施错误的约定:全数大概的荒唐放在第拾2个参数(err)中,结果放在第1个参数中。假若传入了err,意味着执行进度中爆发了不当,这时通过task.throw()正确输出错误对象;要是没有不当发生,data被流传task.next()作为结果储存起来,并继续执行step()。若是result.value不是二个函数,则平昔将其扩散next()方法

近日,那些新版的义务执行器已经足以用来全部的异步义务了。在Node.js环境中,若是要从文件中读取一些多少,须要在fs.readFile()外围创立1个包装器(wrapper),并回到二个与fetchData()类似的函数

let fs = require("fs");
 function readFile(filename) {
  return function(callback) {
   fs.readFile(filename, callback);
  };
}

readFile()接受2个文本名作为参数,再次来到3个能够实行回调函数的函数。回调函数被平昔传入fs.readFile()方法,读取达成后会执行它

run(function*() {
 let contents = yield readFile("config.json");
 doSomethingWith(contents);
 console.log("Done");
});

在那段代码中没有别的回调变量,异步的readFile()操作却寻常履行,除了yield关键字外,别的代码与一同代码完全等同,只可是函数执行的是异步操作。所以依据千篇一律的接口,能够编写一些读起来像是同步代码的异步逻辑

自然,那么些示例中利用的形式也有缺点,也等于不能够百分之百确认函数中回到的其他函数一定是异步的。着眼当下,最根本的是能分晓职务执行进度背后的理论知识

上述那篇详谈ES6中的迭代器(Iterator)和生成器(Generator)正是作者分享给我们的全部内容了,希望能给大家贰个参照,也意在大家多多接济脚本之家。

你恐怕感兴趣的小说:

相关文章

网站地图xml地图