Promise处理异步操作

Promise
在Node.js 中用 Q 实现Promise – Callbacks之外的另一种选择
nodejs promise for q.js
利用q.js实现node 常用api的promise化
q

javascript中处理异步一般都是使用回调函数callback来解决的。但是这种写法很容易写出金字塔形的代码,可读性差。而是用promise链式的代码无疑更容易维护。

首先看一下ES6中的promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function promiseTest() {
var result = 0;
console.log(new Date()+' result: '+result);
var p = new Promise(function(resolve, reject){
//模拟耗时操作,1秒后响应
setTimeout(function(){
result = Math.round(Math.random() * 1000);
if(result > 500){
//得到返回值
resolve(result);
}else{
//抛出异常
reject(result);
}
}, 1000);
});
p.then(function(val){
console.log(new Date()+' resolve result: '+result);
}).catch(function(error){
console.log(new Date()+' reject result: '+result);
});
}

也可以使用q.js来实现promise
首先需要安装q.js

1
npm install q --save

然后可以用他来进行改造默认的callback风格函数。例如nodejs中对文件的读取操作:

1
2
3
4
5
6
7
8
9
var fs = require('fs');

var filename = 'test.txt';
var encoding = 'utf-8';

fs.readFile(filename, encoding, function(err, data){
if (err) throw err;
console.log(data)
});

  • 使用nfcall,nfapply

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var Q = require('q');

    //nfcall与nfapply类似,区别则是传入参数的格式不同
    //nfcall的传入参数不定
    //nfapply的传入参数为数组
    var q_nfcall_test = Q.nfcall(fs.readFile, filename,encoding);
    var q_nfapply_test = Q.nfapply(fs.readFile, [filename,encoding]);

    q_nfcall_test.then(function(result){
    console.log('q_nfcall_test resolve result: '+result);
    },function(error){
    console.log('q_nfcall_test reject result: '+error);
    });

    q_nfapply_test.then(function(result){
    console.log('q_nfapply_test resolve result: '+result);
    },function(error){
    console.log('q_nfapply_test reject result: '+error);
    });
  • 使用denodeify:

    1
    2
    3
    4
    5
    6
    var q_denodeify_test = Q.denodeify(fs.readFile);
    q_denodeify_test(filename, encoding).then(function(result){
    console.log('q_denodeify_test resolve result: '+result);
    },function(error){
    console.log('q_denodeify_test reject result: '+error);
    });
  • 使用makeNodeResolver:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var q_makeNodeResolver_test = function(filename, encoding){
    var deferred = Q.defer();
    fs.readFile(filename, encoding, deferred.makeNodeResolver());
    return deferred.promise;
    };

    q_makeNodeResolver_test(filename, encoding).then(function(result){
    console.log('q_makeNodeResolver_test resolve result: '+result);
    },function(error){
    console.log('q_makeNodeResolver_test reject result: '+error);
    });
  • 使用deferd:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    function q_deferred_test(){
    var result = 0;
    var deferred = Q.defer();
    setTimeout(function(){
    result = Math.round(Math.random() * 1000);
    if(result > 500){
    deferred.resolve(result);
    }else{
    deferred.reject(result);
    }
    }, 1000);
    return deferred.promise;
    }

    q_deferred_test().then(function(result){
    console.log(new Date()+' resolve result: '+result);
    },function(error){
    console.log(new Date()+' reject result: '+error);
    });

可以写出链式操作:

1
2
3
4
5
6
7
8
9
10
11
function q_delay_test(value){
return Q.delay(value, value * 1000);
}

q_delay_test(1).then(function(result){
console.log(new Date()+' q_delay_test result: '+result);
//返回值传入下一个promise中
return q_delay_test(result);
}).then(function(result){
console.log(new Date()+' q_delay_test result: '+result);
});

如果需要并行操作,而返回值各不相干,可以使用all方法:

1
2
3
4
5
6
var q_all_test = Q.all([
q_delay_test(1),
q_delay_test(2)
]);

q_all_test.then(console.log,console.error);

all方法返回的返回值为一个数组,数组的值为all里面各个函数的返回值。如果需要精确的得到返回值,可以使用spread方法:

1
2
3
4
q_all_test.spread(function(a,b){
console.log(new Date()+':'+a);
console.log(new Date()+':'+b);
});