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

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的异步有所领悟。编码快乐!

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

  1. js系列教程2-对象、构造函数、对象属性全解

    全栈工程师开发手册 (作者:栾鹏) 快捷链接: js系列教程1-数组操作全解 js系列教程2-对象和属性全解 js系列教程3-字符串和正则全解 js系列教程4-函数与参数全解 js系列教程5-容器和算 ...

  2. js系列教程1-数组操作全解

    全栈工程师开发手册 (作者:栾鹏) 快捷链接: js系列教程1-数组操作全解 js系列教程2-对象和属性全解 js系列教程3-字符串和正则全解 js系列教程4-函数与参数全解 js系列教程5-容器和算 ...

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

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7723174.html 前面讲到:Java IO编程全解(二)--传统的BIO编程 为了解决同步阻塞I/O面临 ...

  4. js原型、原型链、作用链、闭包全解

    https://www.2cto.com/kf/201711/698876.html [对象.变量] 一个对象就是一个类,可以理解为一个物体的标准化定义.它不是一个具体的实物,只是一个标准.而通过对象 ...

  5. Js基础知识1-数组操作全解

    数组操作全解 js变量类型 var string; var name = "student",age=12; //underfined.null.boolean.string.nu ...

  6. es6常用功能与异步详解(JS高级面试题)

    callback hell方法的使用 可读性不友好 function loadImg(src,callback,fail){ var img = document.createElement('img ...

  7. js的dom对象(带实例超详细全解)

    js的dom对象(带实例超详细全解) 一.总结 一句话总结: 1.DOM中的方法区分大小写么? 解答:区分 2.DOM中元素和节点的关系式什么? 解答:元素就是标签,节点中有元素节点,也是标签,节点中 ...

  8. js系列教程11-json、ajax(XMLHttpRequest)、comet、SSE、WebSocket全解

    js系列教程11-json.ajax(XMLHttpRequest).comet.SSE.WebSocket全解:https://blog.csdn.net/luanpeng825485697/art ...

  9. 转:web前端面试题合集 (Javascript相关)(js异步加载详解)

    1. HTTP协议的状态消息都有哪些? 1**:请求收到,继续处理2**:操作成功收到,分析.接受3**:完成此请求必须进一步处理4**:请求包含一个错误语法或不能完成5**:服务器执行一个完全有效请 ...

随机推荐

  1. [GDAL]编译64位GDAL1.10

    环境VS2010,swigwin-2.0.11 1. 打开nmake.opt文件,找到SWIG=swig.exe这一句,假如没有将swig的目录添加到环境变量中,那么将这句后面的swig.exe修改为 ...

  2. jsoup做http接口测试

    本文转载张飞的博客http://www.cnblogs.com/zhangfei/p/4359408.html在此感谢博主的分享! 最早用Jsoup是有一个小的爬虫应用要写,发现Jsoup较HttpC ...

  3. 3.10 Templates -- Development Helpers

    一.Development Helpers Handlebar和Ember有好多个辅助器可以使模板开发更容易. 这些辅助器输出变量到浏览器的控制台,或者从模板中激活debugger. 二.Loggin ...

  4. 005-matlab2018a安装破解

    1.下载地址: 百度云下载链接:https://pan.baidu.com/s/1uTYAxVX1_Hx6nbsgf4W4kA 密码:asrw 官网下载地址: 2.解压. 3.双击setup.exe后 ...

  5. 13信号signal

    信号 传送给进程的事件通知,完成异步通信 信号的产生 1.程序错误:硬件异常,除数为0,等 2.外部事件:定时器事件,按键中断(ctrl+c)等 3.显示请求:调用 kill,  raise 等信号发 ...

  6. 前端学习笔记之css清除浮动float的七种常用方法总结和兼容性处理

    在清除浮动前我们要了解两个重要的定义: 浮动的定义:使元素脱离文档流,按照指定方向发生移动,遇到父级边界或者相邻的浮动元素停了下来. 高度塌陷:浮动元素父元素高度自适应(父元素不写高度时,子元素写了浮 ...

  7. Django快速搭建博客系统

    Django快速搭建博客系统 一.开发环境 Windows 7(64bit) python 3.6   https://www.python.org/ Django 2.0  https://www. ...

  8. 项目:Android平台txt阅读软件

    项目:Android平台txt阅读软件 项目组成员:20145107李长达.20145110屠轶成.20145120黄玄曦.20145122程智崟 Need: 从古至今,阅读都是人类生活中的一大部分, ...

  9. HeyWeGo小组团队项目管理

    HeyWeGo团队小组项目管理 项目内容 使用java程序开发一款扫雷游戏 实现计划 第一周 明确项目内容.目标.分工,以及完成前期的一些准备 建立新的小组博客以及创建新的代码托管链接 第二周 实行初 ...

  10. 真实机下 ubuntu 18.04 安装GPU +CUDA+cuDNN 以及其版本选择(亲测非常实用)【转】

    本文转载自:https://blog.csdn.net/u010801439/article/details/80483036 ubuntu 18.04 安装GPU +CUDA+cuDNN : 目前, ...