ES6-Promise

前言

ES6已经出了好几年,最近才逐渐开始学ES6甚是愧疚;提起ES6,相信大部分人第一时间想起的是Promise,Promise一出场就是牛逼之气充斥天地,如狼似虎放荡不羁,在前端技术高速更新的这年代不学Promise还真不好意思在别人面前说会异步;刚开始学Promise的时候真是一头雾水,完全不懂为什么社区要提出这种异步解决方案,直至前前后后读了五六次阮一峰老师写的ES6入门中的Promise章节才有点明白为什么Promise会是Promise,书读百遍,其义自见,每次看Promise都会有不同的收获,沉淀了一个月,我觉得自己对Promise也有了一定的了解,是时候写篇博客了。

回调地狱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
select(params, function (err, A) {
if (err) {
console.log(err);
};
//something
other(A, function (err, B) {
if (err) {
console.log(err);
};
//something
another(B, function (err, C) {
if (err) {
console.log(err);
};
console.log(C);
});
});
})

暑假的时候也用Node写了点异步回调,但回调很深,看着很难受,就像这仅仅是三层回调嵌套就已经开始恶心了,继续增加回调不仅维护难,更是增加心智压力,看看Promise的写法

1
2
3
4
5
6
7
8
9
10
11
select(params).then(A => {
//something
return other(A);
}).then(B => {
//something
return another(B);
}).then(C => {
console.log(C);
}).catch(err => {
console.log(err);
})

顿时那种累赘烦杂的感觉就没了,仿佛在写同步一样,心智压力大减;那好,现在开始讲Promise

Promise是什么

  • 看了那么多与Promise有关的博客或文章,发现都大同小异,写得比较好的还是阮一峰老师的,所以这里就概括一下阮一峰老师写的Promise再加上自己的一些见解:

Promise是个对象,但简单点说它是一个容器,装着的是异步操作的结果(成功或失败),也可称为状态机;Promise对象有两个特点,1、对象的状态不受外界影响:Promise代表着一个异步操作,有三种状态:pending(进行中)resolved(成功)rejected(失败),除异步操作,外界无法改变这个状态;2、一旦状态改变,就不会再变:意思是如果一旦确定Promise是什么状态,resolvedrejected,就凝固当前状态,不会再发生改变。

怎样写Promise

1
2
3
4
5
6
7
8
9
let promise = () => {
return new Promise((resolve, reject) => {
//something
if(xxx)
resolve(suc);
else
reject(err);
})
}

如上所示就是一个简单的Promise例子,上面说过,Promsie本身就是一个对象,所以会用到new来生成它,Promise的构造函数需要传入两个参数resolvereject,这两个参数分别是函数,当异步操作结束后,把结果传出去,resolve 传的是成功后的结果,reject 传的是操作失败报错;而调用则用 then 来表示:

1
2
3
4
5
promise().then(suc => {
//something
}, err => {
//something
});

then 也可以接收一个或两个参数,第一个参数是执行成功后的调用,即构造Promise对象时传入resolve 的结果,第二个参数是执行失败的调用,即构造Promise是reject 传入的报错;也可以只有一个参数,用来执行成功后的调用,但是要在then 后面用catch 捕捉错误,不然不会返回错误信息,用catch 的好处是,不管有多少个then,只要有一个catch就可以捕捉以上的所有错误信息,不必每个then里面都带第二个错误参数。

1
2
3
4
5
6
7
8
9
promise().then(A => {
return other Promise
}).then(B => {
return another Promise
}).then(C => {
//something
}).catch(err => {
console.log(err);
})

这里采用的是链式结构,每次调用then里都返回一个新的Promise对象,最后用catch将上面可能抛出的错误捕捉,是一种近似同步的写法

  • 经典实例:
    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
    27
    28
    29
    const getJSON = function(url) {
    const promise = new Promise(function(resolve, reject){
    const handler = function() {
    if (this.readyState !== 4) {
    return;
    }
    if (this.status === 200) {
    resolve(this.response);
    } else {
    reject(new Error(this.statusText));
    }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();
    });
    return promise;
    };
    getJSON("/posts.json").then(function(json) {
    console.log('Contents: ' + json);
    }, function(error) {
    console.error('出错了', error);
    });

实现Ajax操作可以说是应用Promise较多的地方,基本我看的每篇与Promise相关的博客里都会用到实现Ajax操作一实例,在这个实例中,getJSON对XMLhttpRequest对象封装,在现实Promise的时候将数据加载到XMLHttpRequest中,更是给Promise返回了resolvereject

Promise.all 和 Promise.race

  • Promise.all:用来将多个Promise实例包装成一个新的Promise实例,接收的参数是一个数组,Promise.all([]),返回的resolve里的结果也是一个数组。唯有等参数中所有的Promise都执行完成后才会执行then,也唯有所有的Promise都是resolve才是resolve

  • Promise.race:同样是用来将多个Promise实例包装成一个新的Promise实例,和Promise.all唯一不同的是,一旦参数中的Promise完成,整个Promise就以第一个完成的状态走下去。

结语

  • Promise的内容还有很多,上面写这些只是写基础的,阮一峰老师写的ES6真是经典,值得我辈精读!结尾引用一只诚信肥宅写的骚操作,渲染html至md:
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    const fs = require('then-fs')
    , dist = '/home/myblog'
    , src = '/home/myblog-markdown'
    , path = require('path')
    // render 可以渲染 markdwon 字符串
    , render = require('./render')
    fs.readdir(src).then(files => {
    // 文件夹下有 a.md 和 b.md 将其 map 成绝对路径:
    // /home/myblog-markdown/a.md 和 /home/myblog-markdown/b.md
    return files.map(e => path.join(src, e))
    }).then(files => {
    // 这里是绝对路径
    let readAllFiles = files.map(file => {
    return fs.readFile(file).then(data => {
    let html = render(data.toString());
    return {
    html: html,
    filePath: file.replace('.md', '.html')
    }
    });
    });
    return Promise.all(readAllFiles)
    }).then(htmlDatas => {
    let saving = htmlDatas.map(data => {
    let { html, filePath } = data;
    return fs.writeFile(filePath, html);
    });
    return Promise.all(saving);
    }).then(allDone => {
    console.log('All Success')
    }).catch(err => {
    console.log(err);
    })

https://eczn.coding.me/blogs/fc68a92a80335f1c38d81f77e4852e61/