回调函数和异步编程是JavaScript的特点之一,但是JavaScript中传统的回调函数编写太麻烦,如果嵌套的回调函数层级过多,容易产生“回调地狱”的现象,代码变得非常难看。因此Promise在ES6被引入用来解决这个问题。
Promise的基本方法
const promise1 = new Promise((resolve, reject) => {
// some thing
if(/* some */)
resolve(/* some */)
else
reject(/* some */)
});
promise1.then(v => {
// resolve函数
// do some
}, e => {
// reject函数
// do some
})
代码中上面是创建Promise的过程。
创建Promise时接受一个函数作为参数,这个函数就是作为Promise异步执行的主要函数。
(resolve, reject) => {
// some thing
if(/* some */)
resolve(/* some */)
else
reject(/* some */)
}
执行结束后,执行resolve(成功)或者reject(失败)这两个函数。
这两个函数就定义在.then的参数中。
如果Promise没有执行resolve(成功)或者reject(失败),那么.then中的内容永远不会执行,这样就实现了异步操作。
promise函数的异步特点
Promise一个很有意思的特点,就是Promise函数中resolve(成功)或者reject(失败)是异步的:
const promise1 = new Promise((resolve, reject) => {
reject('fail');
console.log("promise1");
});
promise1.then(v => console.log("then1"), e => console.log(e))
浏览器执行结果:
我们先调用reject,然后此时函数应该跳转到then中的reject函数执行。但是并没有。
程序先输出了promise1然后再是fail。这说明resolve和reject是异步的,执行到resolve或reject时标记状态,然后继续执行完promise函数再进入then中。
throw类似reject,但有区别
throw通常情况下可以看作reject。
const promise1 = new Promise((resolve, reject) => {
console.log("promise1");
reject('fail');
});
const promise2 = new Promise((resolve, reject) => {
console.log("promise2");
throw 'fail';
});
promise2.then(v => console.log("then1"), e => console.log(e))
上面的代码中,promise1和promise2的唯一区别是reject换成了throw,但效果是相同的,reject函数成功捕获了错误,把它当成是一个reject来处理。
但throw的一个很大区别是,在promise函数中会同步执行,不会等待promise函数执行完毕再进入then。
const promise1 = new Promise((resolve, reject) => {
reject('fail');
console.log("promise1");
});
const promise2 = new Promise((resolve, reject) => {
throw 'fail';
console.log("promise2");
});
promise2.then(v => console.log("then1"), e => console.log(e))
我们把reject和throw上调了,先抛出错误再输出。
这时候promise函数中的console.log就没有机会输出了。
这是由于考虑到reject和throw的语义是不一样的:
throw表示函数执行出现了错误,不应该继续执行。
reject表示执行失败,但是不表示有错误。
then的链式执行
const promise1 = new Promise((resolve, reject) => {
console.log("promise1");
resolve("ok1");
});
const promise2 = new Promise((resolve, reject) => {
resolve('ok2');
console.log("promise2");
})
promise1.then(v => {
console.log("then1")
return promise2;
}).then(v => console.log("then2", v), e => console.log("then2", e));
在then中返回另一个Promise,或者抛出错误(throw),后面的then就会接受另一个Promise的状态,调用resolve或者reject。
此时还可以看到,在创建Promise后,promise函数接已经在执行了,我们在then中返回的promise2时早就执行完毕,仅仅是把resolve或者reject状态传递给了下一个then。
then中即使不返回另一个Promise,也可以链式执行:
Promise.resolve(2).then(() => console.log(1)).then(() => console.log(2))
这是因为resolve和reject函数默认返回一个promise。我们看到浏览器每次执行完毕后输出的最后结果就是这个promise。
(看上面浏览器输出的最后一行)
注意这个promise并不代表我们一开始建立的promise。
如果我们指定了返回值,那么相当于返回默认的promise时附带了参数:
reject可被省略
then中的reject函数可以省略:
const promise1 = new Promise((resolve, reject) => {
resolve('ok');
console.log("promise1");
});
promise1.then(v => console.log("then1"))
但是如果此时调用reject也无法被then读取。
catch可替代reject
promise1.then(v => console.log("then1"), e => console.log(e));
promise1.then(v => console.log("then1")).catch(e => console.log(e))
这两种形式是相等的。
reject和catch的链式捕获
在链式then中,rejcect和catch可以捕获之前任一层出现的错误。
const promise1 = new Promise((resolve, reject) => {
throw new Error('fail');
console.log("promise1");
});
const promise2 = new Promise((resolve, reject) => {
resolve('ok');
console.log("promise2");
})
promise1.then(v => {
console.log("then1")
return promise2;
}).then(v => console.log("then2", v), e => console.log("then2", e));
浏览器执行结果:
从结果中我们看到,在链式then中,如果出现了reject或者throw,后面所有then中的resolve都不会被执行。上面then1就没被执行,且promise2虽然是resolve状态,但是依然没有被后面then2中的resolve接受,反而执行了then2的reject。
改成catch的写法:
promise1.then(v => {
console.log("then1")
return promise2;
}).then(v => console.log("then2", v)).catch(e => console.log("then2", e));
在执行完reject之后,promise就变为了resolve状态。如果后面还有链式then,就可以执行reslove了。
可以看到,在执行完reject之后,后面then中的resolve也被执行了。
最后,文章中描述的resolve状态,实际上应该是fulfilled(已完成)状态,为了方便理解,我就直接使用了resolve来描述。
更多资料
在这里介绍的仅仅是Promise中很少的性质,如果想了解Promise的更多用法,可以看看下面的资料:
阮一峰 《ES6标准入门》Promise部分
es6.ruanyifeng.com/#docs/promi…
MDN文档 Promise部分
developer.mozilla.org/zh-CN/docs/…
文章为完全原创,转载请标注来源。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!