当前位置: 首页 > news >正文

网站落地页是什么意思seo点击工具帮你火21星热情

网站落地页是什么意思,seo点击工具帮你火21星热情,ios网站开发,企业网站建设方案 word目录 1. 传统方法 2. 基本概念 2.1异步 2.2回调函数 2.3Promise 3. Generator 函数 3.1协程 3.2协程的 Generator 函数实现 3.3Generator 函数的数据交换和错误处理 3.4异步任务的封装 4. Thunk 函数 4.1参数的求值策略 4.2Thunk 函数的含义 4.3JavaScript 语言的…目录 1. 传统方法 2. 基本概念 2.1异步 2.2回调函数 2.3Promise 3. Generator 函数 3.1协程 3.2协程的 Generator 函数实现 3.3Generator 函数的数据交换和错误处理 3.4异步任务的封装 4. Thunk 函数 4.1参数的求值策略 4.2Thunk 函数的含义 4.3JavaScript 语言的 Thunk 函数 4.4Thunkify 模块 4.5Generator 函数的流程管理 4.6Thunk 函数的自动流程管理 5. co 模块 5.1基本用法 5.2co 模块的原理 5.3基于 Promise 对象的自动执行 5.4co 模块的源码 5.5处理并发的异步操作 5.6实例处理 Stream 1. 传统方法 ES6 诞生以前异步编程的方法大概有下面四种。 回调函数 事件监听 发布/订阅 Promise 对象 Generator 函数将 JavaScript 异步编程带入了一个全新的阶段 2. 基本概念 2.1异步 所谓异步简单说就是一个任务不是连续完成的可以理解成该任务被人为分成两段先执行第一段然后转而执行其他任务等做好了准备再回过头 执行第二段。 比如有一个任务是读取文件进行处理任务的第一段是向操作系统发出请求要求读取文件。然后程序执行其他任务等到操作系统返回文件再接 着执行任务的第二段处理文件。这种不连续的执行就叫做异步。 相应地连续的执行就叫做同步。由于是连续执行不能插入其他任务所以操作系统从硬盘读取文件的这段时间程序只能干等着。 2.2回调函数 JavaScript 语言对异步编程的实现就是回调函数。所谓回调函数就是把任务的第二段单独写在一个函数里面等到重新执行这个任务的时候就直接 调用这个函数。回调函数的英语名字 callback 直译过来就是重新调用。 读取文件进行处理是这样写的。 fs.readFile(/etc/passwd, utf-8, function (err, data) { if (err) throw err; console.log(data); }); 上面代码中 readFile 函数的第三个参数就是回调函数也就是任务的第二段。等到操作系统返回了 /etc/passwd 这个文件以后回调函数才会执行。 一个有趣的问题是为什么 Node 约定回调函数的第一个参数必须是错误对象 err 如果没有错误该参数就是 null 原因是执行分成两段第一段执行完以后任务所在的上下文环境就已经结束了。在这以后抛出的错误原来的上下文环境已经无法捕捉只能当作参 数传入第二段。 2.3Promise 回调函数本身并没有问题它的问题出现在多个回调函数嵌套。假定读取 A 文件之后再读取 B 文件代码如下。 fs.readFile(fileA, utf-8, function (err, data) { fs.readFile(fileB, utf-8, function (err, data) { // ... }); }); 不难想象如果依次读取两个以上的文件就会出现多重嵌套。代码不是纵向发展而是横向发展很快就会乱成一团无法管理。因为多个异步操作形 成了强耦合只要有一个操作需要修改它的上层回调函数和下层回调函数可能都要跟着修改。这种情况就称为回调函数地狱callback hell。 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); }); 上面代码中我使用了 fs-readfile-promise 模块它的作用就是返回一个 Promise 版本的 readFile 函数。Promise 提供 then 方法加载回调函数 catch 方法捕捉执行过程中抛出的错误。 可以看到Promise 的写法只是回调函数的改进使用 then 方法以后异步任务的两段执行看得更清楚了除此以外并无新意。 Promise 的最大问题是代码冗余原来的任务被 Promise 包装了一下不管什么操作一眼看去都是一堆 then 原来的语义变得很不清楚。 那么有没有更好的写法呢 3. Generator 函数 3.1协程 传统的编程语言早有异步编程的解决方案其实是多任务的解决方案。其中有一种叫做协程coroutine意思是多个线程互相协作完成异步任 务。 协程有点像函数又有点像线程。它的运行流程大致如下。 第一步协程 A 开始执行。 第二步协程 A 执行到一半进入暂停执行权转移到协程 B 。 第三步一段时间后协程 B 交还执行权。 第四步协程 A 恢复执行。 上面流程的协程 A 就是异步任务因为它分成两段或多段执行。 举例来说读取文件的协程写法如下。 function* asyncJob() { // ...其他代码 var f yield readFile(fileA); // ...其他代码 } 上面代码的函数 asyncJob 是一个协程它的奥妙就在其中的 yield 命令。它表示执行到此处执行权将交给其他协程。也就是说 yield 命令是异步两个 阶段的分界线。 协程遇到 yield 命令就暂停等到执行权返回再从暂停的地方继续往后执行。它的最大优点就是代码的写法非常像同步操作如果去除 yield 命令 简直一模一样。 3.2协程的 Generator 函数实现 Generator 函数是协程在 ES6 的实现最大特点就是可以交出函数的执行权即暂停执行。 整个 Generator 函数就是一个封装的异步任务或者说是异步任务的容器。异步操作需要暂停的地方都用 yield 语句注明。Generator 函数的执行方 法如下。 function* gen(x) { var y yield x 2; return y; } var g gen(1); g.next() // { value: 3, done: false } g.next() // { value: undefined, done: true } 上面代码中调用 Generator 函数会返回一个内部指针即遍历器 g 。这是 Generator 函数不同于普通函数的另一个地方即执行它不会返回结 果返回的是指针对象。调用指针 g 的 next 方法会移动内部指针即执行异步任务的第一段指向第一个遇到的 yield 语句上例是执行到 x 2 为 止。 换言之 next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法会返回一个对象表示当前阶段的信息 value 属性和 done 属性。 value 属性是 yield 语句后面表达式的值表示当前阶段的值 done 属性是一个布尔值表示 Generator 函数是否执行完毕即是否还有下一个阶段 3.3Generator 函数的数据交换和错误处理 Generator 函数可以暂停执行和恢复执行这是它能封装异步任务的根本原因。除此之外它还有两个特性使它可以作为异步编程的完整解决方案函 数体内外的数据交换和错误处理机制。 next 返回值的 value 属性是 Generator 函数向外输出数据 next 方法还可以接受参数向 Generator 函数体内输入数据。 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 } 上面代码中第一 next 方法的 value 属性返回表达式 x 2 的值 3 。第二个 next 方法带有参数 2 这个参数可以传入 Generator 函数作为上个阶段 异步任务的返回结果被函数体内的变量 y 接收。因此这一步的 value 属性返回的就是 2 变量 y 的值。 Generator 函数内部还可以部署错误处理代码捕获函数体外抛出的错误。 function* gen(x){ try { var y yield x 2; } catch (e){ console.log(e); } return y; } var g gen(1); g.next(); g.throw(出错了); // 出错了 上面代码的最后一行Generator 函数体外使用指针对象的 throw 方法抛出的错误可以被函数体内的 try...catch 代码块捕获。这意味着出错的代 码与处理错误的代码实现了时间和空间上的分离这对于异步编程无疑是很重要的。 3.4异步任务的封装 下面看看如何使用 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); } 上面代码中Generator 函数封装了一个异步操作该操作先读取一个远程接口然后从 JSON 格式的数据解析信息。就像前面说过的这段代码非常像 同步操作除了加上了 yield 命令。 执行这段代码的方法如下 var g gen(); var result g.next(); result.value.then(function(data){ return data.json(); }).then(function(data){ g.next(data); }); 上面代码中首先执行 Generator 函数获取遍历器对象然后使用 next 方法第二行执行异步任务的第一阶段。由于 Fetch 模块返回的是一个 Promise 对象因此要用 then 方法调用下一个 next 方法。 可以看到虽然 Generator 函数将异步操作表示得很简洁但是流程管理却不方便即何时执行第一阶段、何时执行第二阶段 4. Thunk 函数 Thunk 函数是自动执行 Generator 函数的一种方法。 4.1参数的求值策略 Thunk 函数早在上个世纪 60 年代就诞生了。 那时编程语言刚刚起步计算机学家还在研究编译器怎么写比较好。一个争论的焦点是求值策略即函数的参数到底应该何时求值。 var x 1; function f(m){ return m * 2; } f(x 5) 上面代码先定义函数 f 然后向它传入表达式 x 5 。请问这个表达式应该何时求值 一种意见是传值调用call by value即在进入函数体之前就计算 x 5 的值等于 6再将这个值传入函数 f 。C 语言就采用这种策略。 f(x 5) // 传值调用时等同于 f(6 另一种意见是“传名调用”call by name即直接将表达式 x 5 传入函数体只在用到它的时候求值。Haskell 语言采用这种策略。 f(x 5) // 传名调用时等同于 (x 5) * 2 传值调用和传名调用哪一种比较好 回答是各有利弊。传值调用比较简单但是对参数求值的时候实际上还没用到这个参数有可能造成性能损失。 function f(a, b){ return b; } f(3 * x * x - 2 * x - 1, x); 上面代码中函数 f 的第一个参数是一个复杂的表达式但是函数体内根本没用到。对这个参数求值实际上是不必要的。因此有一些计算机学家倾向 于传名调用即只在执行时求值。 4.2Thunk 函数的含义 编译器的“传名调用”实现往往是将参数放到一个临时函数之中再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数 function f(m) { return m * 2; } f(x 5); // 等同于 var thunk function () { return x 5; }; function f(thunk) { return thunk() * 2; } 上面代码中函数 f 的参数 x 5 被一个函数替换了。凡是用到原参数的地方对 Thunk 函数求值即可。 这就是 Thunk 函数的定义它是“传名调用”的一种实现策略用来替换某个表达式。 4.3JavaScript 语言的 Thunk 函数 JavaScript 语言是传值调用它的 Thunk 函数含义有所不同。在 JavaScript 语言中Thunk 函数替换的不是表达式而是多参数函数将其替换成一 个只接受回调函数作为参数的单参数函数。 // 正常版本的readFile多参数版本 fs.readFile(fileName, callback); // Thunk版本的readFile单参数版本 var Thunk function (fileName) { return function (callback) { return fs.readFile(fileName, callback); }; }; var readFileThunk Thunk(fileName); readFileThunk(callback) 上面代码中 fs 模块的 readFile 方法是一个多参数函数两个参数分别为文件名和回调函数。经过转换器处理它变成了一个单参数函数只接受回调 函数作为参数。这个单参数版本就叫做 Thunk 函数。 任何函数只要参数有回调函数就能写成 Thunk 函数的形式。下面是一个简单的 Thunk 函数转换器。 // ES5版本 var Thunk function(fn){ return function (){ var args Array.prototype.slice.call(arguments); return function (callback){ args.push(callback); return fn.apply(this, args); } }; }; // ES6版本 const Thunk function(fn) { return function (...args) { return function (callback) { return fn.call(this, ...args, callback); } }; }; 使用上面的转换器生成 fs.readFile 的 Thunk 函数 var readFileThunk Thunk(fs.readFile); readFileThunk(fileA)(callback); 下面是另一个完整的例子 function f(a, cb) { cb(a); } const ft Thunk(f); ft(1)(console.log) // 1 4.4Thunkify 模块 生产环境的转换器建议使用 Thunkify 模块。 首先是安装 $ npm install thunkify 使用方式如下 var thunkify require(thunkify); var fs require(fs); var read thunkify(fs.readFile); read(package.json)(function(err, str){ Thunkify 的源码与上一节那个简单的转换器非常像 function f(a, b, callback){ var sum a b; callback(sum); callback(sum); } var ft thunkify(f); var print console.log.bind(console); ft(1, 2)(print); // 3 上面代码中由于 thunkify 只允许回调函数执行一次所以只输出一行结果 4.5Generator 函数的流程管理 你可能会问 Thunk 函数有什么用回答是以前确实没什么用但是 ES6 有了 Generator 函数Thunk 函数现在可以用于 Generator 函数的自动流 程管理。 Generator 函数可以自动执行 function* gen() { // ... } var g gen(); var res g.next(); while(!res.done){ console.log(res.value); res g.next(); } 上面代码中Generator 函数 gen 会自动执行完所有步骤。 但是这不适合异步操作。如果必须保证前一步执行完才能执行后一步上面的自动执行就不可行。这时Thunk 函数就能派上用处。以读取文件为 例。下面的 Generator 函数封装了两个异步操作。 var fs require(fs); var thunkify require(thunkify); var readFileThunk thunkify(fs.readFile); var gen function* (){ var r1 yield readFileThunk(/etc/fstab) console.log(r1.toString()); var r2 yield readFileThunk(/etc/shells); console.log(r2.toString()); }; 上面代码中 yield 命令用于将程序的执行权移出 Generator 函数那么就需要一种方法将执行权再交还给 Generator 函数。 这种方法就是 Thunk 函数因为它可以在回调函数里将执行权交还给 Generator 函数。为了便于理解我们先看如何手动执行上面这个 Generator 函数 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); }); }); 上面代码中变量 g 是 Generator 函数的内部指针表示目前执行到哪一步。 next 方法负责将指针移动到下一步并返回该步的信息 value 属性和 done 属性。 仔细查看上面的代码可以发现 Generator 函数的执行过程其实是将同一个回调函数反复传入 next 方法的 value 属性。这使得我们可以用递归来自 动完成这个过程 4.6Thunk 函数的自动流程管理 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(); } function* g() { // ... } run(g); 上面代码的 run 函数就是一个 Generator 函数的自动执行器。内部的 next 函数就是 Thunk 的回调函数。 next 函数先将指针移到 Generator 函数的 下一步 gen.next 方法然后判断 Generator 函数是否结束 result.done 属性如果没结束就将 next 函数再传入 Thunk 函数 result.value 属性否则就直接退出。 有了这个执行器执行 Generator 函数方便多了。不管内部有多少个异步操作直接把 Generator 函数传入 run 函数即可。当然前提是每一个异步操 作都要是 Thunk 函数也就是说跟在 yield 命令后面的必须是 Thunk 函数。 var g function* (){ var f1 yield readFileThunk(fileA); var f2 yield readFileThunk(fileB); // ... var fn yield readFileThunk(fileN); }; run(g); 上面代码中函数 g 封装了 n 个异步的读取文件操作只要执行 run 函数这些操作就会自动完成。这样一来异步操作不仅可以写得像同步操作而且 一行代码就可以执行。 Thunk 函数并不是 Generator 函数自动执行的唯一方案。因为自动执行的关键是必须有一种机制自动控制 Generator 函数的流程接收和交还程 序的执行权。回调函数可以做到这一点Promise 对象也可以做到这一点 5. co 模块 5.1基本用法 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 函数返回一个 Promise 对象因此可以用 then 方法添加回调函数。 co(gen).then(function (){ console.log(Generator 函数执行完成); }); 上面代码中等到 Generator 函数执行结束就会输出一行提示 5.2co 模块的原理 为什么 co 可以自动执行 Generator 函数 前面说过Generator 就是一个异步操作的容器。它的自动执行需要一种机制当异步操作有了结果能够自动交回执行权。 两种方法可以做到这一点。 1回调函数。将异步操作包装成 Thunk 函数在回调函数里面交回执行权。 2Promise 对象。将异步操作包装成 Promise 对象用 then 方法交回执行权。 co 模块其实就是将两种自动执行器Thunk 函数和 Promise 对象包装成一个模块。使用 co 的前提条件是Generator 函数的 yield 命令后面只 能是 Thunk 函数或 Promise 对象。如果数组或对象的成员全部都是 Promise 对象也可以使用 co详见后文的例子。 介绍了基于 Thunk 函数的自动执行器。下面来看基于 Promise 对象的自动执行器。这是理解 co 模块必须的。 5.3基于 Promise 对象的自动执行 还是沿用上面的例子。首先把 fs 模块的 readFile 方法包装成一个 Promise 对象。 var fs require(fs); var readFile function (fileName){ return new Promise(function (resolve, reject){ fs.readFile(fileName, function(error, data){ if (error) return reject(error); resolve(data); }); }); }; var gen function* (){ var f1 yield readFile(/etc/fstab); var f2 yield readFile(/etc/shells); console.log(f1.toString()); console.log(f2.toString()); }; 然后手动执行上面的 Generator 函数。  var g gen(); g.next().value.then(function(data){ g.next(data).value.then(function(data){ g.next(data); }); }); 手动执行其实就是用 then 方法层层添加回调函数。理解了这一点就可以写出一个自动执行器。 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); 上面代码中只要 Generator 函数还没执行到最后一步 next 函数就调用自身以此实现自动执行 5.4co 模块的源码 co 就是上面那个自动执行器的扩展它的源码只有几十行非常简单。 首先co 函数接受 Generator 函数作为参数返回一个 Promise 对象。 function co(gen) { var ctx this; return new Promise(function(resolve, reject) { }); } 在返回的 Promise 对象里面co 先检查参数 gen 是否为 Generator 函数。如果是就执行该函数得到一个内部指针对象如果不是就返回并将 Promise 对象的状态改为 resolved 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); }); } 接着co 将 Generator 函数的内部指针对象的 next 方法包装成 onFulfilled 函数。这主要是为了能够捕捉抛出的错误。 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) { return reject(e); } next(ret); } }); } 最后就是关键的 next 函数它会反复调用自身 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)) ); } 上面代码中 next 函数的内部代码一共只有四行命令。 第一行检查当前是否为 Generator 函数的最后一步如果是就返回。 第二行确保每一步的返回值是 Promise 对象。 第三行使用 then 方法为返回值加上回调函数然后通过 onFulfilled 函数再次调用 next 函数。 第四行在参数不符合要求的情况下参数非 Thunk 函数和 Promise 对象将 Promise 对象的状态改为 rejected 从而终止执行 5.5处理并发的异步操作 co 支持并发的异步操作即允许某些操作同时进行等到它们全部完成才进行下一步。 这时要把并发的操作都放在数组或对象里面跟在 yield 语句后面。 // 数组的写法 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); 下面是另一个例子。 co(function* () { var values [n1, n2, n3]; yield values.map(somethingAsync); }); function* somethingAsync(x) { // do something async return y } 上面的代码允许并发三个 somethingAsync 异步操作等到它们全部完成才会进行下一步 5.6实例处理 Stream Node 提供 Stream 模式读写数据特点是一次只处理数据的一部分数据分成一块块依次处理就好像“数据流”一样。这对于处理大规模数据非常有 利。Stream 模式使用 EventEmitter API会释放三个事件 data 事件下一块数据块已经准备好了。 end 事件整个“数据流”处理“完了。 error 事件发生错误。 使用 Promise.race() 函数可以判断这三个事件之中哪一个最先发生只有当 data 事件最先发生时才进入下一个数据块的处理。从而我们可以通过 一个 while 循环完成所有数据的读取。 const co require(co); const fs require(fs); const stream fs.createReadStream(./les_miserables.txt); let valjeanCount 0; co(function*() { while(true) { const res yield Promise.race([ new Promise(resolve stream.once(data, resolve)), new Promise(resolve stream.once(end, resolve)), new Promise((resolve, reject) stream.once(error, reject)) ]); if (!res) { break; } stream.removeAllListeners(data); stream.removeAllListeners(end); stream.removeAllListeners(error); valjeanCount (res.toString().match(/valjean/ig) || []).length; } console.log(count:, valjeanCount); // count: 1120 }); 对于每个数据块都使用 stream.once 方法在 data 、 end 、 error 三个事件上添加一次性回 调函数。变量 res 只有在 data 事件发生时才有值然后累加每个数据块之中 valjean 这个词出现的次数。 总结 本博客源于本人阅读相关书籍和视频总结创作不易谢谢点赞支持。学到就是赚到。我是歌谣励志成为一名优秀的技术革新人员。 欢迎私信交流一起学习一起成长。 推荐链接 其他文件目录参照 “睡服“面试官系列之各系列目录汇总建议学习收藏
http://www.zqtcl.cn/news/772534/

相关文章:

  • 化妆品网站模板网络营销的网站分类有哪些
  • 广州网站建设程序员培训wordpress 微信 抓取
  • 毕设给学校做网站个人店铺logo
  • 中国做w7的网站宿迁网站建设价位
  • 网站建设售后服务合同百度关键词排名点击器
  • 编辑网站用什么软件推广是什么
  • 北京模板开发建站做网站赚钱的点在哪里
  • 网站建设价格兴田德润i网址多少wordpress主题汉化是什么意思
  • 用最少的钱做网站根据域名查询网站名称
  • 网站开发答辩难点网站返回按钮设计
  • 鹤壁做网站优化建设银行理财产品网站
  • 电子商务类网站模板自学网站建设基本流程
  • 无锡网站制作的公司上海企业服务公司
  • 做h5小程序的网站搜索引擎营销案例
  • 订餐网站开发方案查询网站是否正规
  • 建站论坛图片生成器免费
  • 怎么做自己的店铺网站博物馆门户网站建设优势
  • 专业旅游培训网站建设应用之星 wordpress
  • 青海媒体网站建设公司深圳网站建设推广优化公司
  • 网站开发 价格跨境支付互联互通
  • 织梦 修改网站logo营销型网站设计的内容
  • 电商网站运营策划做网站CentOS还是win好
  • 小型企业网站模板企业网站seo点击软件
  • 提供邯郸企业建网站网站图片上怎么做弹幕效果
  • 滨州做网站的wordpress如何添加商桥
  • 网站登录密码忘记网站开发营业执照申请
  • 电商网站设计思路音乐推广平台有哪些
  • 网站建设傲鸿网站链轮内有死链
  • 哪些网站可以做微商品牌宣传网站怎么不花钱做排名 知乎
  • 上传了网站源码怎么做wordpress加百度广告代码出问题