异步编程总结-7
发布于 2 个月前 作者 halu886 594 次浏览 来自 分享

该文章阅读需要5分钟,更多文章请点击本人博客halu886

流程控制库

wind

这里还有一种完全不同的异步编程方案wind。前身是jscex,为Javascript提供一种拓展monadic。

以下以一个冒泡进行举例

var compare = function(x,y){
    return x-y;
}

var swap = function(a,j,j){
    var t = a[i];a[i]=a[j];a[j]= t;
}

var bubbleSort = function(array){
    for(var i= 0;i<array.length;i++){
        for(var i = 0;i<array.length;i++){
            if(compare(array[j],array[j+1]>0)){
                swap(array,j,j+1);
            }
        }
    }
};

现在我们要添加一个动画的需求,我们可以在swap()中添加动画的逻辑。但是动画的渲染得是延时的,但是js中延时方法setTimeout()是异步的。那么就会出现以下两个问题。

  • 动画执行时无法停止排序算法的执行。
  • 排序算法执行会启用更多的动画。

wind在这个问题体现了它的独特魅力。

var compare= function(x,y){
    return x-y;
}

var swapAsync = eval(Wind.compile("async",function(a,i,j){
    $await(Wind.Async.sleep(20));
    var t = a[i];a[i]= a[j];a[j] = t;
    paint(a); //重绘数组
}))

var bubbleSort = eval(Wind.compile("async",function(array){
    for(var i = 0;i<array.length;i++){
        for(var j = 0;j<array.length-i-1;j++){
            if(compare(array[j],array[j+1]>0){
                $await(swapAsync(array,j,j+1));
            })
        }
    }
}));

以上代码实现了暂停20秒,绘制动画。但是并没有因为引入异步流程库而变得异步化,逻辑并没有因为异步而拆分。

以上代码引入了一些新东西

  • eval(Wind.compile(“async”,function(){}));
  • $await();
  • Wind.Async.sleep(20);

以下我们将详细介绍

异步任务定义

在Wind 中,巧妙的运用了 eval 的访问上下文的特性,Wind.compile() 将function编译,同时Wind.Async.sleep则对setTimeOut进行了封装。

$await()与任务模型

在定义完异步方法后,通过$await等待异步方法结束,但是它只不过是一个等待的占位符,告诉编译器需要到这里等待。

$await的参数一个任务对象,当任务结束后执行后续的操作。任何异步操作都可以转换为任务。wind基于任务模型进行实现的。

以下将fs.readFile()转化为一个任务

var Wind = require("wind");

var Task = Wind.Async.Task;

var readFileAsync = function(file,encoding){
    return Task.create(function(t){
        fs.readFile(file,encoding,function(err,file){
            if(err){
                t.complete("failure",err);
            }else{
                t.complete("success",file);
            }
        });
    });
}

除了使用eval(wind.compile(“async”,function(){}))生成异步任务,标准的异步任务创建方式是Task.create()方式,通过偏函数转换得到真正的任务。当异步完成时通过complete传递failure/success。异常则可以通过try/catch捕获。

例如,调用readFileAsync()得到一个任务

var task = readFileAsync('file1.txt','utf-8');

var serial = eval(Wind.compile("async",function(){
    var file1 = $await(readFileAsync('file1.txt','utf-8');
    console.log(file1);
    var file2 = $await(readFileAsync('file2.txt','utf-8'));
    console.log(file2);
    try{
        var file3 = $await(readFileAsync('file3.txt','utf-8'));
    }catch{
        console.log(err);
    }
}));

serial().start();

// file1
// file2
// { [Error:ENOENT,open 'file3.txt'] errno:34,code:'ENOENT',path:'file3.txt'}

异步通常能够会立即返回,wink做到了不阻塞CPU却阻塞业务代码。

va4 parallel = eval(Wind.compile("async",function(){
    var result = $await(Task.whenAll({
        file1:readFileAsync('file1.txt','utf-8'),
        file2:readFileAsync('file2.txt','utf-8')
    }));
    console.log(result.file1);
    console.log(result.file2);
}));
//file1
//file2

wind配置了Task.whenAll处理并发,通过$await关键字配置当所有任务都完成则向下执行。

异步方法转换为辅助函数

可以看到除了task.compile(“async”,function(){})稍微冗长外,其他的基本基本上和同步方法风格一致。

如同Promise/Deferred模式可以让异步编程模型变得更简单,wind则可以让编程风格同步化,不过我们得提前将异步方法任务化,这也是对Promise/Deferred的封装。

wind提供了两个方法进行辅助转换。

Wind.Async.Binding.fromCallback
Wind.Async.Binding.fromStandard

在Node中,回调传值的方式有两种,一个是没有异常的传递,参数就是异步的结果。

fs.exists("/etc/passwd",function(exists){});

Wind.Async.Binding.fromCallback就是为转换这一种的。

另一种则是带异常的调用,按照规范第一个参数是异常。

fs.readFile("/etc/passwd",function(err,data){});

Wind.Async.Binding.fromStandard就是为转换这一种的。

那么readFileAsync则只需要一行代码则可以实现。

以上知识点均来自<<深入浅出Node.js>>,更多细节建议阅读书籍:-)

5 回复

啥时代了还用eval ? 流控制目前最棒的是 rxjs

@waitingsong 我这是阅读《深入浅出Node.js》的总结,那本书还是14年出版的呢

@halu886

14年到现在,异步的处理已经翻天覆地了

callback -> fibers -> promise -> async\await

不过了解一下旧的处理方案也不算错,不过你这个库,我确实没听说过

看了一下这个库,已经7年没更新了。。。

@CoderIvan 你搜的哪个 rxjs 哟。。 。 npmjs.org 上面搜 第一个就是 v6.5.2

@CoderIvan 同意, 现在主流基本上是用async/await, 在多个异步并发时配上promise.all基本能解决大部分异步场景, 最主要的是这是ts的语法糖,开箱即用

回到顶部