优雅异步编程方式
in Javascript with 0 comment

优雅异步编程方式

in Javascript with 0 comment

并发

同一时间处理多件事情的能力,滚动屏幕,动态请求内容,然后进行渲染

var count = 0,ary = [],total = 30;
for(var i = 0;i < total;i++)
{
    (function(index)
    {
        ajax.(url,function(result)
        {
            ary[index] = result;
            count++;
            if(count == total)
            {
                paint();
            }
        })
    })(i)
}

对于并发处理的多个任务,如果任务与任务之间没有联系,那么这些任务是可以并行执行的,如果任务与任务之间有依赖,那么这些任务就需要串行执行了,因此对于并发的任务处理可以总结为两种情况,一种是并行的任务处理,另一种是串行的任务处理。

并发模型

为什么Javascript不选择多线程?

  1. 简单,不涉及共享数据与锁的处理(最大的共享数据就是DOM,试想下如果多个线程共同操作DOM的可怕后果)
  2. 浏览器的环境并没有很强的并行需求

事件循环

while(1)
{
    var event = EventQueue.shift();
    event.handler();
}

为什么要使用异步?
Javascript引擎的并发模型是单线程的事件循环模型,如果对于耗时长的操作使用同步,会使单线程的事件循环模型性能大大降低。

如何获取异步的结果?
为了能获取异步操作的结果,程序使用callback的方式,当操作完成后,往事件队列push一个事件,当事件循环处理这个事件时,发起异步操作时传入的callback就会被调用。

Callback的问题

  1. 代码结构不清晰
  2. 函数复用性差

如何优雅的处理异步

  1. Thunk https://github.com/thunks/thunks
  2. Promise https://www.promisejs.org/
  3. RxJS http://reactivex.io/
  4. generator

Thunk

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script src="./thunk.js"></script>
<script>
function get(url,callback)
{
    setTimeout(function()
    {
        callback([1,2,3])
    },500)
}


var thunk = thunks();

var t = thunk(1);
thunk(function(callback)
{
    //return 1
    callback(null,1)
})(function(e,v)
{
    console.log(e);
    console.log(v);
    return thunk(function(callback)
    {
        setTimeout(function()
        {
            callback(null,3)
        },1000)
    })
})(function(e,v)
{
console.log(e);
console.log(v)
})
/*
var getThunk = thunk.thunkify(get,'url');
getThunk(function(data)
{
    console.log(data)
})
*/
</script>
</body>
</html>

Promise

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script>
function get(url)
{
    return new Promise(function(resolve,reject)
    {
        setTimeout(function()
        {
            resolve([1,2,3])
        },500)
    })
}

function handle(p)
{
    return new Promise(function(resolve,reject)
    {
        p.then(function(data)
        {
            for(var i = 0,len =data.length;i < len;i++)
            {
                data[i]++;
            }
            resolve(data);
        })
    })
}

var p = get('url');
p = handle(p);
p.then(function(data)
{
    console.log(data);
})
</script>
</body>
</html>

RxJS

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<button id="a" page="1"></button>
<script src="./rx.js"></script>
<script>
//Example 1
/*var comments = [
    {contentid : '1',content : 'afasfsadf',uin : '1'},
    {contentid : '1',content : 'afasfsadf',uin : '2'},
    {contentid : '1',content : 'afasfsadf',uin : '3'},
    {contentid : '1',content : 'afasfsadf',uin : '4'},
    {contentid : '1',content : 'afasfsadf',uin : '4'}
]
function getComment(page,pagesize)
{
    return new Promise(function(res,rej)
    {
        setTimeout(function()
        {
            res(comments)
        },500)
    });
}
function getNick(uin)
{
    return new Promise(function(res,rej)
    {
        res('w');
    })
}
function getLevel(uin)
{
    return new Promise(function(res,rej)
    {
        res(10);
    })
}
var clicks = Rx.Observable.fromEvent(document.getElementById('a'),'click')
.pluck('target','attributes','page','value')
.flatMap(function(data)
{
    return getComment(data,10)
})
.flatMap(function(data)
{
    var ary = [];
    for(var i = 0;i < data.length;i++)
    {
        (function(index)
        {
            ary[index] = new Promise(function(res,rej)
            {
            
                Promise.all([getNick(data[index].uin),getLevel(data[index].uin)]).then(function(nickandlevel)
                {
                    data[index].nick = nickandlevel[0];
                    data[index].level = nickandlevel[1];
                    res(data[index]);
                })
            })
        })(i)    
    }
    return  Promise.all(ary);
})
clicks.subscribe(function(data)
{
    console.log(data)
})*/

//Example 2
/*var ob1 = Rx.Observable.create(function(observer)
{
    observer.onNext(1);
})

var ob2 = Rx.Observable.create(function(observer)
{
    observer.onNext(2);
})

Rx.Observable.merge(ob1,ob2).bufferWithCount(2).subscribe(function(data)
{
    console.log(data);
})*/
</script>
</body>
</html>

generator

<!DOCTYPE HTML>
<html lang="zh">
<head>
</head>
<body>
<script src="./co.js"></script>
<script>
//Example 1
/*
function sleep(t)
{
    return function(callback)
    {
        setTimeout(function()
        {
            callback()
        },t)
    }
}

function get(url)
{
    return function(callback)
    {
        setTimeout(function()
        {
            callback([1,2,3])
        },500)
    }
}

co(function *()
{
    yield sleep(2000);
    var data = yield get('url');
    console.log(data);
})*/

//Example 2
/*
var script = "onmessage = function(e) { postMessage('msg from worker'); }";
function CoWorker(script)
{
    return function(callback)
    {
        var blob = new Blob([script]);

        var blobURL = window.URL.createObjectURL(blob);

        var worker = new Worker(blobURL);
        worker.onmessage = function(e)
        {
            callback(e.data);
        };
        worker.postMessage(''); 
    }    
}

co(function *()
{
    var data = yield CoWorker(script)
    console.log(data)
})*/

</script>
</body>
</html>
//co.js
function co(generator) {

    var gen = generator();
    function next(result) {
        
        var step = gen.next(result);
        if (!step.done) {
            step.value(next);
        } 
    }
    next();
}
Responses