Meteor.js异步全解

翻译来源: http://phucnguyen.info/blog/everything-you-need-to-know-about-async-meteor/

posted in Web Development on October 26, 2014 by Phuc Nguyen

SHARE

Meteor是运行在Node.js之上的。这意味着I/O行为,比如读文件或者发送网络请求不会阻塞整个程序。事实上,当异步行为执行结束后,我们可以提供回调。好了解不?下面会有图画解释。

假设我们想读一个加密过的文件,接着解密出内容:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

var aes = Meteor.require(‘aes-helper‘)

, fs = Meteor.require(‘fs‘);

var getSecretData = function(key) {

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) console.log(err);

else console.log( ‘Secret: ‘ + aes.decrypt(res, key) );

}

};

getSecretData(‘my-secret-key‘);

而更通用,多样的事件序列长成这样:

事件序列只是等待执行的函数队列而已。每当调用函数时,就放到事件序列里边去。

当我们执行函数getSecretData去解密并打印文档内容时,函数readFile就会被调用,出现在事件序列里边。

读文件函数readFile并不关心他后面执行什么,这哥们只是告诉系统发送文件,接着就滚蛋了!

分分秒,readFile结束。‘callback’回调这货就会跳进事件序列:

很快,收到文件后,英雄归来,完成后面的所有工作。

很好很有用吧?! 可是如果任务更复杂,需要多层异步该怎么办?结果就成这吊样:

真蛋疼!异步流程控制代码太变态了,无法阅读和维护!要是getSecretData能同步返回内容就好了,像这样:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/* This code looks nicer, but sadly it doesn‘t work */

getSecretData = function(key) {

var decryptedSecret;

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) console.log(err);

else decryptedSecret = aes.decrypt(res, key);

}

return decryptedSecret;  // undefined  <-- oops!

};

// So sweet. We have getSecretData return the value, then print it out, all synchronously.

// If only life were that simple...

var result = getSecretData(‘my-secret-key‘); // undefined

console.log(result); // undefined

可惜,这样的代码不可行,因为getSecretData会在readFile结束前就执行了,直接返回undefined。解决这问题,非英雄莫属,那就是Fiber-王者归来!

接触Fiber,他是个可以容纳多个函数的无敌英雄!

Fiber其实就是特别的容器函数。他可以跟普通函数一样被扔进事件序列。但他也别有魔力:可以在任意执行点暂停,跳出执行序列,任意时间后再回来,任由程序员调戏!Fiber暂停时,流程控制权就接力到事件序列里边的下一个函数(普通函数,新Fiber函数都可以)。

你可能已经看到好处了:如果Fiber含有费时的I/O行为,它可以跳出事件序列,等待结果。同时,我们也可以运行序列里的下一个函数。人生苦短,时间珍贵!I/O结束,Fiber可以再转回来,从上次执行点接着来.下面是用Fiber写的代码:

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

var Fiber = Npm.require(‘fibers‘);

// Our Fiber-powered getSecretData function

getSecretData = function(key) {

var fiber = Fiber.current; // get the currently-running Fiber

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) console.log(err);

else fiber.run( aes.decrypt(res, key) ); // resume execution of this fiber. The argument passed

// to fiber.run (i.e. the secret data) will become

// the value returned by Fiber.yield below

}

// halt this Fiber for now. When the execution is resumed later, return whatever passed to fiber.run

var result = Fiber.yield();

return result;

};

// We wrap our code in a Fiber, then run it

Fiber(function() {

var result = getSecretData(‘my-secret-key‘);

console.log(result);  // the decrypted secret

}).run();

可能还不好理解是吧?下面的图标更直观:

Fiber发现yield时,他会休息一下!

调用run()就回复Fiber的执行,任何传递到run()将会变成yield()的返回值。

你还叫?“看起来还行。但是yield run这货,我感觉有点奇葩”。

我同意!还有比Fiber更猛的大神。那就是Future!

你可以把Future当作Fiber的抽象。这货提供了更强大的API,像是驯养的Fiber。

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

var Future = Npm.require(‘fibers/future‘);

// Our Future-powered getSecretData function

getSecretData = function(key) {

var future = new Future; // create a new, bright future

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) console.log(err);

else future.return( aes.decrypt(res, key) );  // signal that the future has finished (resolved)

// the passed argument  (the decrypted secret)

// will become the value returned by wait() below

}

return future;  // we return the future instance so other code can wait()  for this future

};

// The future method is added to the prototype object of every function

// Calling future() on a function will return a Fiber-wrapped version of it

(function() {

// we wait for the future to finish. While we‘re waiting, control will be yielded

// when this future finishes, wait() will return the value passed to future.return()

var result = getSecretData(‘my-secret-key‘).wait();

console.log(result);

}.future()) ();

嘿咻!上面的列子都是可以自由修改getSecretData函数的。可是当异步函数不好修改是怎么办?比如第三方API。小事一桩,不需要修改,包一下就行了!

JavaScript

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// A native, garden-variety async function

getSecretData = function(key, callback) {

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) throw new Error(err.message);

else callback && callback( null, aes.decrypt(res, key)  );

}

};

// Let‘s wrap it up

// What returned here is actually a future instance. When the async operation completes,

// this future instance will be marked as finished and the result of the async operation

// will become the value returned by wait()

var getSecretDataSynchronously =  Future.wrap(getSecretData);

(function() {

// we call wait() on the returned future instance to, well, wait for this future to finish

// and get the result later when it does

var result = getSecretDataSynchronously (‘my-secret-key‘).wait();

console.log(result);

}.future()) ();

嗯,好像每次调用下wait就可以了。还是有点烦!

哈哈,用Meteor.warapAsync,他还可以更简便! 

JavaScript

1

2

3

4

5

6

7

8

9

10

getSecretData = function(key, callback) {

fs.readFile(‘/secret_path/encrypted_secret.txt‘, ‘utf8‘, function(err, res) {

if (err) throw new Error(err.message);

else callback && callback( null, aes.decrypt(res, key)  );

}

};

var getSecretDataSynchronously =  Meteor.wrapAsync(getSecretData);

var result = getSecretDataSynchronously(key);  // <-- no wait() here!

return result;

实际上,除了吸引眼球,我们还有一些异步相关的话题可以聊聊:

– – –

Future.wrap 和 Meteor.wrapAsync不是万能药

他们只适合原生的纯异步函数。就是有回调,返回error,result那种函数。还有,他们只能在服务器端有用,因为yielding在客户端不行-没用Fibers。

– – –

Meteor.wrapAsync会把你纯真的函数变成双半脸!!!

幸运的是,双面函数也不太坏。有时候他们很有用,他们可以同步调用(上面几种方案),也可以异步调用(传一个回调函数)。

服务器端,HTTP.call, collection.insert/update/remove都已经内置了这种包裹方式。比如HTTP,如果直接调用,方法会等到response返回;如果提供回调函数,他就直接跳出,等网络返回response再条用回调函数。

客户端,由于不能用阻塞,只能提供回调函数。

– – –

Fiber 瑕疵

默认,客户端调用是在Fiber里的-----一次一个。这个Fiber可以访问当前用户的环境变量(比如Meteor.userId())。这也会产生问题:

1)服务器端,同步调用HTTP.call这类方法会阻塞当前用户的后续方法。这未必是什么好事情。如果后续方法跟当前方法无关的话,其实可以使用this.unblock(),这样其他方法调用就会在新的Fiber里进行

JavaScript

1

2

3

4

5

6

Meteor.methods({

requestSecret: function() {

this.unblock();

return HTTP.call(‘GET‘, ‘http://www.nsa.gov/top-secrets‘);

}

});

2) “Meteor代码必须在Fiber里边执行”

似曾相识不?错误总是不断当你调用第三方异步API时发生。不能这样搞,因为回掉函数在Fiber之外执行了,无法访问环境变量。一种解决方案就是用Meteor.bindEnvironment包一下,他能返回Fiber包过新版函数。方案2就是用Meteor.wrapAsync(实际wrapAsyncn内部就是调用的bindEnvironment ).



希望你对Meteor的异步有所领悟。编码快乐!

时间: 2024-12-25 16:33:40

Meteor.js异步全解的相关文章

Java IO编程全解(三)——伪异步IO编程

转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽. 下面,我们结合连接模型图和源

妹子UI-yepnope.js使用详解及示例分享(异步加载数据)

yepnope.js 是一个超高速的按条件异步加载资源的加载器,允许你只加载使用到的资源(css及js). yepnope.js的一个典型实例: ? 1 2 3 4 5 yepnope({ test : Modernizr.geolocation, yep : 'normal.js', nope : ['polyfill.js', 'wrapper.js'] }); 此实例表示如果Modernizr.geolocation为真的时候加载normal.js,为假则加载polyfill.js及wra

POST &amp;amp; GET &amp;amp; Ajax 全解

GET&POST&Ajax 全解 一.POST和GET的差别 GET:GET方法提交数据不安全,数据置于请求行.客户段地址栏可见:GET方法提交的数据限制大小在255个字符之内.參数直接跟在URL后面清晰可见,该http请求的body部分也是空的.仅仅有head部分显示了一个http的基本信息. POST:POST方法提交的数据置于消息主体内,client不可见,POST提交的数据大小没有限制. POST方式发送的http请求,參数不是跟在URL后面的,而是存放在http请求的body部分

触碰jQuery:AJAX异步详解

触碰jQuery:AJAX异步详解 传送门:异步编程系列目录…… 示例源码:触碰jQuery:AJAX异步详解.rar AJAX 全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML).它并非一种新的技术,而是以下几种原有技术的结合体. 1)   使用CSS和XHTML来表示. 2)   使用DOM模型来交互和动态显示. 3)   使用XMLHttpRequest来和服务器进行异步通信. 4)   使用javascript来绑定和调用.

PHP漏洞全解(三)-客户端脚本植入

本文主要介绍针对PHP网站的客户端脚本植入攻击方式.所谓客户端脚本植入攻击,是指将可以执行的脚本插入到表单.图片.动画或超链接文字等对象内.当用户打开这些对象后,黑客所植入的脚本就会被执行,进而开始攻击. 客户端脚本植入(Script Insertion),是指将可以执行的脚本插入到表单.图片.动画或超链接文字等对象内.当用户打开这些对象后,攻击者所植入的脚本就会被执行,进而开始攻击. 可以被用作脚本植入的HTML标签一般包括以下几种: 1.<script>标签标记的javascript和vb

js异步编程

前言 以一个煮饭的例子开始,例如有三件事,A是买菜.B是买肉.C是洗米,最终的结果是为了煮一餐饭.为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A.B做完),这三件事是相关的,抽象起来有三种场景. 顺序做 先买菜,再买肉,最后洗米, 即 A->B->C. 并发做 买菜,买肉,洗米,一起做. 交集做 买菜,买肉必须先做完,才能做洗米. 场景就是这样,接下来就是如何考虑用js实现. function A(callback){ setTimeout(function(){ c

node.js安装全攻略

node.js学习也有近一年之久了,自开始以来,能遇到各种问题,就拿安装来说,总能遇到稀奇古怪的问题,但每次谷歌,或者百度,都能找到解决办法,逐渐也就养成了百度或者谷歌的毛病! 正常安装 第一步:下载安装文件. 第二步:安装nodejs.下载完成之后,双击 node-v0.x.xx-x86.msi,开始安装nodejs,默认是安装在C:\Program Files\nodejs下面 第三步:安装相关环境. 打开C:\Program Files\nodejs目录你会发现里面自带了npm,直接用np

Bootstrap transition.js 插件详解

Bootstrap transition.js 插件详解 时间 2015-01-27 12:12:00 博客园-原创精华区 原文  http://www.cnblogs.com/xyzhanjiang/p/4252513.html 主题 Bootstrap JavaScript Bootstrap 自带的 JavaScript 插件的动画效果几乎都是使用 CSS 过渡实现的,而其中的 transition.js 就是为了判断当前使用的浏览器是否支持 CSS 过渡.下面先来简单了解下 CSS 过渡

触碰jQuery:AJAX异步详解(转)

AJAX 全称 Asynchronous JavaScript and XML(异步的 JavaScript 和 XML).它并非一种新的技术,而是以下几种原有技术的结合体. 1)   使用CSS和XHTML来表示. 2)   使用DOM模型来交互和动态显示. 3)   使用XMLHttpRequest来和服务器进行异步通信. 4)   使用javascript来绑定和调用. 通过AJAX异步技术,可以在客户端脚本与web服务器交互数据的过程中使用XMLHttpRequest对象来完成HTTP请