在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开胃菜 - 四种方法
Javascript异步编程的4种方法 Javascript语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。 *** "同步模式"就是上一段的模式,后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的; *** "异步模式"则完全不同,每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
一、回调函数function f1(callback){ setTimeout(function () { // ----> 将耗时的操作推迟执行,什么垃圾的初级思想,当然不可行 // f1的任务代码 callback(); }, 1000); }
二、事件监听另一种思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。 // 当f1发生done事件,就执行f2。 }, 1000); }
三、发布/订阅这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。 就是多出了一个“订阅中心”统一管理信号。 // f2向"信号中心"jQuery订阅"done"信号。
四、Promises对象 (重点,接下来的内容)f1的回调函数f2,回调函数变成了链式写法。 f1().then(f2); f1要进行如下改写: function f1(){ var dfd = $.Deferred(); setTimeout(function () { // f1的任务代码 dfd.resolve(); }, 500); return dfd.promise; }
Promise 对象 - 最后也是最流行的一个ECMAScript 6 入门 - Promise 对象
一、Promise 的含义所谓 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。 Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
(1)对象的状态不受外界影响。
只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。 这也是
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。
- - 只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。 如果改变已经发生了,你再对 【晚一步也能得到信息:这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的】
有了 此外,
- 首先,无法取消 - 其次,如果不设置回调函数, - 第三,当处于 如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署
二、基本用法
创造了一个 /**
Promise内部的setTimeout这样的函数, 执行成功:走then这个策略; 执行失败:应该走error的策略。 /* 延迟执行 */
then的方法执行优先级略低。 /* 立即执行 */
function loadImageAsync(url) {
参数promise 如果调用
注意,这时 如果 如果 Detail: const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) // 1. 注意:调用 new Promise((resolve, reject) => { resolve(1); // 立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务【变为:return resolve(1)就不会执行后面的了】 console.log(2); // 仍然会执行,并且还是首先打印出来【后继操作应该放到
/* implement */
Ref:nodejs与Promise的思想碰撞【有必要一读】
《深入掌握 ECMAScript 6 异步编程》系列文章
Generator 函数
一、基本概念Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。 Generator 函数有多种理解角度。
- 状态机 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
- 遍历器对象生成函数 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。 返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
- 两大特点 形式上,Generator 函数是一个普通函数,但是有两个特征。 (1) (2) 函数体内部使用 function* helloWorldGenerator() {
yield 'hello'; // 状态一
yield 'world'; // 状态二
return 'ending'; // 状态三
}
var hw = helloWorldGenerator(); // 不会立即执行,返回的是:一个指向内部状态的指针对象
下一步,必须调用遍历器对象的 调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。
以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。
value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;
done属性是一个布尔值,表示是否遍历结束。
二、yield表达式 与 next()
提供了一种可以暂停执行的函数。 (1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。 (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。 (3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。 (4)如果该函数没有return语句,则返回的对象的value属性值为undefined。 为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
Generator 函数可以不用 因为:函数 注意 如若不然:瞧这个反例子 var arr = [1, [[2, 3], 4], [5, 6]]; var flat = function* (a) { a.forEach(function (item) { if (typeof item !== 'number') { yield* flat(item); } else { yield item; } }); }; for (var f of flat(arr)){ console.log(f); }
function* outer() { yield 'open' yield inner() // --> (A) yield 'close' } function* inner() { yield 'hello!' } (A) 加了星号,意思为:看这个表达式的本质,而非表象。 == yield inner() == var gen = outer() gen.next() // -> 'open' gen.next() // -> a generator,这是表象 gen.next() // -> 'close' == yield* inner() == var gen = outer() gen.next() // -> 'open' gen.next() // -> 'hello!',这是表象背后的本质 gen.next() // -> 'close'
Thunk 函数一、基本概念"传值调用"(call by value) "传名调用"(call by name) 编译器的"传名调用"实现,往往是将参数放到一个临时函数之中,再将这个临时函数【Thunk函数】传入函数体。 function f(m){ return m * 2; } f(x + 5); // 等同于 var thunk = function () { return x + 5; }; function f(thunk){ return thunk() * 2; }
二、两大特点在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。 // 正常版本的readFile(多参数版本) fs.readFile(fileName, callback); 任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。
三、Thunkify 模块生产环境的转换器,建议使用 Thunkify 模块。
四、Thunk的用法作为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());
co 函数库一、基本概念
比如,有一个 Generator 函数,用于依次读取两个文件。
co 函数库可以让你不用编写 Generator 函数的执行器。
上面代码中,Generator 函数只要传入 co 函数,就会自动执行。
co 函数返回一个 Promise 对象,因此可以用 then 方法添加回调函数。
上面代码中,等到 Generator 函数执行结束,就会输出一行提示。
当异步操作有了结果,能够自动交回执行权。 两种方法可以做到这一点。
co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。 使用 co 的前提条件是,Generator 函数的 yield 命令后面,只能是 Thunk 函数或 Promise 对象。
(1) 基于 Thunk 函数的自动执行器。【上一部分】 (2) 基于 Promise 对象的自动执行器。【如下】 只要 Generator 函数还没执行到最后一步,next 函数就调用自身,以此实现自动执行。 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); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); }; 然后,手动执行上面的 Generator 函数。
手动执行其实就是用 then 方法,层层添加回调函数。理解了这一点,就可以写出一个自动执行器。
async 函数
一、基本认知
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()); }; # 写成 async 函数,就是下面这样 var asyncReadFile = async function (){ var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
var sleep = function (time) { return new Promise(function (resolve, reject) { setTimeout(function () { // 模拟出错了,返回 ‘error’ reject('error'); }, time); }) }; var start = async function () { try { console.log('start'); await sleep(3000); // 这里得到了一个返回错误 // 所以以下代码不会被执行了 console.log('end'); } catch (err) { console.log(err); // 这里捕捉到错误 `error` } };
故,可以理所当然的写在 ..省略以上代码 var start = async function () { for (var i = 1; i <= 10; i++) { console.log(`当前是第${i}次等待..`); await sleep(1000); } };
..省略以上代码 let 一到十 = [1,2,3,4,5,6,7,8,9,10]; // 错误示范 一到十.forEach(function (v) { console.log(`当前是第${v}次等待..`); await sleep(1000); // 错误!! await只能在async函数中运行 }); // 正确示范 for(var v of 一到十) { console.log(`当前是第${v}次等待..`); await sleep(1000); // 正确, for循环的上下文还在async函数中 }
import fs from 'fs'; import path from 'path'; import request from 'request'; var movieDir = __dirname + '/movies', exts = ['.mkv', '.avi', '.mp4', '.rm', '.rmvb', '.wmv']; |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论