一、Promise
promise表示一个异步操作的最终结果,可以理解为状态机,它存在三种不同的状态:
(1)Pending:表示还在执行。
(2)Fulfilled(或者resolved):执行成功。
(3)Rejected:执行失败。
1、将异步方法封装成Promise
1 //Promise的构造函数
2 var promise = new Promise(function (resolve,reject) {
3 if(/*异步操作成功*/){
4 resolve(value);
5 }else{
6 reject(value);
7 }
8 })
9 //使用Promise封装的readFile
10 var fs = require("fs");
11 function readFile_promise(path) {
12 return new Promise(function(resolve, reject) {
13 fs.readFile(path, "utf-8", function(err, data) {
14 if (data) {
15 resolve(data);
16 } else {
17 reject(err);
18 }
19 });
20 });
21 }
其中,resolve和reject是两个函数,resolve函数会在异步操作成功完成时被调用,并将异步操作的返回值作为参数传递到外部;reject则在异步操作出现异常时被调用,会将错误信息作为参数传递出去。即resolve和reject方法没有做多余的操作,仅仅把异步的结果传递出去而已,对于异步结果,是交给then方法来完成的。
2、使用then方法获取结果
1 //处理结构
2 promise.then(function (data) {
3 //success
4 },function (error) {
5 //failure
6 })
7 var pro = readFile_promise('foo.txt');
8 pro.then(function(value) {
9 console.log(value);
10 return readFile_promise('bar.txt');
11 }).then(function(value) {
12 console.log(value);
13 }).catch(function(err) {
14 console.log(err);
15 });//捕获异常
3、Promise的常用API
(1)Promise.resolve:将一个给Promise对象转化为Promise对象。但resolve不能转换一个异步方法,例如readFile方法。
1 var obj = {
2 then: function() {
3 console.log("I am a then method");
4 }
5 }
6 Promise.resolve(obj); //转化后的Promise会自动执行其的then方法
7 //I am a then method
8 var p = Promise.resolve("Hello World");
9 p.then(function (result) {
10 console.log(result);
11 }); //Hello World
(2)Promise.reject:也是返回一个Promise对象,不同之处在于这个Promise的状态为reject,reject方法的参数会作为错误信息传递给then方法。
(3)Promise.all:将多个Promise对象包装成一个Promise。当调用Promise.all时,所有的Promise都已经开始执行了,all方法只是等到全部的Promise完成后,对所有的执行结果做一下包装再返回。
1 var promises = ["foo.txt", "bar.txt", "baz.txt"].map(function(path) {
2 return readFile_promise(path);
3 });
4 Promise.all(promises).then(function(results) {
5 console.log(results); //results的内容是文本文件内容的顺序排列
6 }).catch(function(err) {
7
8 });
(4)Promise.race:接收一个Promise数组作为参数并返回一个新的Promise,数组中的Promise会同时开始执行,race返回的Promise的状态由数组中率先执行完毕的Promise的状态决定。
(5)Promise.catch:Promise在执行中如果出了错误,可以使用throw关键字抛出错误,并且可以使用catch方法进行去捕获;如果不设置任何回调函数捕获错误,Promise内部抛出的错误就无法传递到外部。
1 var promise = new Promise(function(resolve, reject) {
2 throw new Error("get error");
3 });
4 //如果不设置catch函数,上面即使抛出error也不会使进程退出。
5 promise.catch(function(error) {
6 console.log(error);
7 });
4、Promise的不足:Promise的一堆链式调用会让人看起来很麻烦。可以考虑将then方法的回调函数抽取出来。
二、Generator
我们无法控制Promise的执行,新建一个Promise后,其状态自动转化为pending,同时开始执行,直到状态改变后我们才能进行下一步操作。而Generator函数不同,Generator函数可以由用户执行中断或者恢复执行的操作,Generator中断后可以转去执行别的操作,然后再回头从中断的地方恢复执行。
Generator函数和普通函数在外表上最大的区别有两个:
(1)在function关键字和方法名中间有个星号(*)。
(2)方法体中使用“yield”关键字。
1 var fs = require("fs");
2
3 function readFile_promise(path) {
4 return new Promise(function(resolve, reject) {
5 fs.readFile(path, "utf-8", function(err, data) {
6 if (data) {
7 resolve(data);
8 } else {
9 reject(err);
10 }
11 });
12 });
13 }
14
15 function* gen() {
16 var result = yield readFile_promise('../baz.txt');
17 console.log(result);
18 }
19 var g = gen();
20 var result = g.next();
21 //console.log(result);
22 result.value.then(function(data) {
23 g.next(data);
24 });
之所以可以使用Generator函数来处理异步任务,原因有二:
(1)Generator函数可以中断和恢复执行,这个特性有yield关键字来实现。
(2)Generator函数内外可以交换数据,这个特性由next函数来实现。
即Generator函数处理异步任务的核心思想:先将函数暂停在某处,然后拿到异步操作的结果,然后再把这个结果传到方法体内。
三、async
async函数可以看作是自带执行器的Generator函数。await关键字后面往往是一个Promise,如果不是就隐式调用Promise.resolve来转换成一个Promise。
1 var fs = require("fs");
2 async function asyncFunc() {
3
4 return "hello node";
5 }
6 asyncFunc().then(function(data) {
7 console.log(data);
8 });
9
10 function readFile_promise(path) {
11 return new Promise(function(resolve, reject) {
12 fs.readFile(path, "utf-8", function(err, data) {
13 if (data) {
14 resolve(data);
15 } else {
16 reject(err);
17 }
18 });
19 });
20 }
21
22 aysnc
23
24 function readFile() {
25 var result = await readFile_promise("../foo.txt");
26 console.log('test');
27 }
28 readFile();
async函数的优点:可以实现自动执行,无须借助第三方模块等,也免去了Generator函数中一些复杂的概念,async函数的声明和执行与普通同步函数几乎一模一样(除了async和await关键字外)。
async函数也有一些不足的地方,如果我们有很多层的方法调用,最底层的异步操作被封装成了async方法,那么该函数的所有上层方法可能都要变成async方法。