ES6 Promise 简介

来源:互联网 发布:对流取暖器 知乎 编辑:程序博客网 时间:2024/05/21 01:54

简介

这段时间公司上线了个印尼的项目,通过 CMD + seajs + 模版来实现的一个单页应用,然后经过压缩打包,跟起 BUG 来那种痛苦简直是: 不想说,就对了,一万个nnnnnnnnnnnnnm

吐槽归吐槽,现在前端模块化,工程化开发越来越流行,只能说公司还停留在原生硬编程阶段是比较落后的。这个代码还是有很多值得学习的地方的(哪个厂家的就不透露了,你懂的~~~)。


比如今天碰到的跟 Promise 有关的,代码里面可以说大量使用了 Promise,根据这几天的跟进,对 Promise 这个也更加熟悉了点,这里就来进一步的学习,记录下。

言归正传,进入正题 ✈

Promise 对象

顾名思义,从 Promise 的英文字面意思“承诺”来理解,意思就是它会确保在满足条件的情况下一定会去执行相关的逻辑,其实就是我们的异步任务。

语法:

new Promise(function (resolve, reject) { ... });

参数说明:

resolve:代表承诺已经实现会被执行的函数,这个函数可以在接下来的 .then() 中第一个参数去指定;

reject:代表承诺没法实现,被拒绝的状态,则会执行该函数,这个函数是可选项,如果没指定而状态又是reject那便会被后面的.catch捕获处理;

来看一个简单的小示例:(承诺:下午三点钟见)

// promise/index.htmlvar $       = (id) => document.getElementById(id),    debug   = (str) => console.log(str);var isSuccess = true;new Promise((resolve, reject) => {    isSuccess ? resolve('下午三点见!') : reject('今天行程已满,下次见吧!');}).then(msg => debug(msg), msg => alert(msg));

上面代码的执行结果由 isSuccess 来决定,如果承诺可以见面,那么便会执行 resolve
而在控制台输出 :

下午三点见

如果不能兑现承诺便会 alert 出: ‘今天行程已满,下次见吧!’

没法完成承诺

通过上面简单的是否见面的示例演示了如何使用 Promise


了解了 Promise 对象的简单使用,下面看下其常用的几个函数如何使用

Promise 函数

then(resolveFn[, rejectFn]);

这个函数用来创建承诺成功或失败后执行的函数(resolveFn, rejectFn),承诺对象的回调函数里面的两个参数即这个 then 里面对应的函数;

简单示例如上面的‘见面示例’。

另外,.then() 语法支持点式链式语法,比如:promise.then().then().then(); 前提是 then() 前面的 then() 必须返回一个 Promise 对象,否则都会按照承诺兑现来执行,也就是会执行 resolve 函数。

示例:(下午三点见! -> 商讨合作事宜) 两个事务

// promise/index.htmlvar isSuccess = true;new Promise((resolve, reject) => {    isSuccess ? resolve('下午三点见!') : reject('今天行程已满,下次见吧!');}).then(msg => {    debug(isSuccess + ': ' + msg);}, msg => alert(isSuccess + ': ' + msg)).then(() => console.log('then 2 resolve'), () => alert('then 2 reject'))

如果只是上面形式,第一个 .then() 中并没有返回 Promise 对象,不管第一个.then 结果是 resolve 还是 reject,后面的then 执行的永远是第一个参数中的回调。

即:控制台在 then 1 承诺成功或失败后总是输出 :’then 2 resolve’

修正:

.then(msg => {    debug(isSuccess + ': ' + msg);    // 这里返回一个新的 Promise 对象,供紧跟在后面的 then 使用    return new Promise((resolve, reject) => {        resolve('商讨合作事宜');    });}, msg => alert(isSuccess + ': ' + msg)).then((msg) => debug(msg), () => alert('then 2 reject'))

经过上面的修正之后,在 isSuccesstrue 的时候,表示有时间见面并决定会面所谈事务

输出结果:

see talk

从结果可知,第一行是由第一个 .then 承诺成功输出,表示承诺见面,第二行也就是由第二个 .then 的承诺成功的输出,表示要商谈的内容。

使用 .then 的时候只要记住,如果要链式重复使用 then ,就要确保前一个 then 中返回的也是个 Promise 对象。

catch( errorHandler )catch( rejectHandler )

catch 异常捕获函数,如果Promise 对象后面调用了 catch ,那么前面所有函数中的异常都不会被该函数给捕获


除了异常捕获之外,如果.then 中没有指定第二个函数,即 reject 处理,那么当承诺失败时,会默认去执行 catch,或者理解为承诺失败也属于异常情况。

分别看下两个例子

发生异常或错误:(比如:严格模式下变量未声明就直接使用)

// promise/index.html'use strict';var isSuccess = true;new Promise((resolve, reject) => {    a = 19;  // 这里未声明直接使用    isSuccess ? resolve('下午三点见!') : reject('今天行程已满,下次见吧!');}).then(msg => {    debug(isSuccess + ': ' + msg);}).catch(e => alert(e));

假如没有错误发生,或者没有 catch 情况下,会执行 resolve('下午三点见!'),而上例代码结果会是:

a undefined

但是有点奇怪的是,尝试把 a = 19; 这行移到resovlereject 之后

isSuccess ? resolve('下午三点见!') : reject('今天行程已满,下次见吧!');a = 19;  // 这里未声明直接使用

会惊奇的发现并不会弹出上面的报错框,而是控制台正常输出了 ‘true: 下午三点见!’(搜了下相关的解释,不是很理解,记住先吧)。

a = 19; 去掉,isSuccess 改成 false,那么这个时候的会执行 reject 可是then 中又没有传入第二个参数,导致 Promise 中的 reject参数是undefined,因此执行reject('今天行程已满,下次见吧!');也就会出现问题了,并且会被catch捕获到

所以上面✈的关于rejectcatch的说法不太准确,这里更正一下,.then 中没有传入reject承诺失败的情况还是因为错误导致的,被catch给捕获了。

可以把catch去掉验证,看控制台输出结果如下:

uncaught

Promise.all(iterable)

iterable:是一组Promise对象的集合,这个集合会被当作后面的.then中函数的参数。

all函数的描述是,只有在 iterable 中所有的Promise对象执行结果是 resovle 的时候才会执行后面.then中的 resolve,要是其中有一个出现reject那么就会执行.then中的reject

通俗点讲就是:你们(集合)的承诺全部兑现,我(.then)才会兑现承诺,否则只要有一个没有兑现,那么我也就不会兑现我的承诺。

示例:

// promise/index.htmlvar p1 = new Promise(function (resolve, reject) {    resolve('p1-resolve');});var p2 = new Promise(function (resolve, reject) {    resolve('p2-resolve');    // reject('p2-reject');});Promise.all([p1, p2]).then(ps => alert('resolve: ' + ps), ps => alert('reject: ' + ps));

全部resolve,上面输出结果将会是:

all resolve

如果有一个reject

resolve('p2-resolve');// reject('p2-reject');

那么结果是:

all reject

如果说再加入一个 p3-reject,并且放在 p2-reject之前

var p3 = new Promise(function (resolve, reject) {    reject('p3-reject');});

结果会是:

p3 reject

这说明 Promise.all.then 只要遇到第一个 reject 就会结束,然后去执行thenreject,其实这个跟逻辑或和与有点类似,逻辑与:必须全部为真最后结果才真,如果有一个未假后面就不会再执行。

另外会发现,all中的 iterable集合中的值,是由所有all里面的resovle参数组合而成,然后整体传递给.thenresolve/reject函数。

那假如直接使用基本数据呢:

// promise/index.htmlvar p1 = 'Hello',  p2 = 'World', p3 = '!';Promise.all([p1, p2, p3]).then(ps => alert('resolve: ' + ps), ps => alert('reject: ' + ps));

得到的结果会输出:Hello,World,! 其实就是把 [p1, p2, p3] 数组字符串化输出了。

所以,不过你传入all的类型是什么,它都会把其组合成数组传给.then的函数;

比如

对象:var p1 = 'Hello', p2 = {name:'lizc'}, p3 = '!';

结果: resolve: Hello,[object Object],!

数组:var p1 = 'Hello', p2 = [1,2,3], p3 = '!';

结果: resolve: Hello,1,2,3,!

综上所述

all 函数的 iterable

  1. 包含的如果是 Promise对象,那么数据会采用Promise中调用resolvereject时传入的参数

  2. 包含的如果是基本数据类型,那么会以数组成员组合到iterable中,如:Hello,World,!

  3. 包含的如果有对象,那么会将对象toString,即得到[object Object],作为数组成员,如:resolve: Hello,[object Object],!

  4. 包含的如果是数组,则会将数组拆分出成员然后和all的参数数组进行组合成新的数组,如:resolve: Hello,1,2,3,!

并且all参数不论以何种形式组合,都遵循:只要有一个reject就会thenreject,只有全部resolve才会thenresolve,而基本数据类型和对象类型会被认为是resolve

因此,当

var p1 = 'hello', p2 = 'world', p3 = new Promise(function (resolve,reject) { reject('p3-reject'); });

的时候,结果会是 reject: p3-reject

Promise.race(iterable)

使用:

Promise.race([p1, p2]).then();

race 可以理解为竞争,其后的then执行结果是resolve还是reject,取决于[p1, p2]那个先执行完。

即:如果 p1先执行完成为resolvep2后执行完为reject,那么race.then中执行resolve。反之,执行p2中的reject

看个示例:(谁先走问题)

// promise/index.htmlvar p1 = new Promise(function (resolve, reject) {    setTimeout(resolve, 500, 'p1 first');});var p2 = new Promise(function (resolve, reject) {    setTimeout(resolve, 1000, 'p2 first');});var p3 = new Promise(function (resolve, reject) {    setTimeout(resolve, 1500, 'p3 first');});Promise.race([p1, p2, p3]).then(ps => alert('resolve: ' + ps), ps => alert('reject: ' + ps));

上例结果:p1 first

p1 first

这个 race 并不难理解,只要那个任务先执行完就会根据任务承诺结果,去执行对应的race.then中的rejectresolve

Promise 继承自 Function 的属性和函数

继承自 Function 需要注意的是 bind 函数,经常会被 Promise 模式中使用到。

总结

对于 Promise 对象下面的主要也就 then, catch, all, race, 另外就是 resolvereject 这几个函数的使用和结合使用的情况。

最后简单补充下 resolvereject 的使用:

  • Promise.resolve(value)

    这个函数效果就是,将value转换成一个承诺兑现的Promise对象,其实就是必定执行resolvePromise对象。

    比如:

    var p = Promise.resolve('hello world');p.then(value => alert(value)); // => hello world

    如果参数value值是普通类型,会直接将其转成结果为resolvePromise对象;

    如果参数是个带有then函数的对象,那么会根据then中的承诺结果来执行,比如:

    var p = Promise.resolve({    then: function (success, fail) {        success('object with then, success');    }});p.then(value => alert(value)); // 结果:object with then, success

    从实例结果可知 p.then 会把 Promise.resolve()里面的then中的 success/fail参数传递给p.then回调的参数。

  • Promise.reject(value)

    reject 使用方法和 resolve原理一样

    Promise    .reject(throw new Error('rejected, ugly so much !'))    .then(function (error) {        // 这里是 resolve,不会执行    }, function ( error ) {        // 执行这里 reject        alert(error)    });  // 结果:rejected, ugly so much !(因为太丑而被拒绝) 

Promise 对象小研究,到此告终,颇有收获,写着写着感觉自己码字速度也快了不少(撸啊撸,手酸着~~)。

其实对于异步任务执行 ES7 中还更新了 Generatorasync 函数,更加智能好用,有兴趣值得学习学习。

1 0
原创粉丝点击