1. 引入原因
js(nodejs)的耗时请求(如sql和file)都是异步返回。如果调用嵌套太多,则会造成“地狱回调”如下:
sayhello("a", function(err){ sayhello("b", function(err){ sayhello("c", function(err){ sayhello("d", function(err){ sayhello("e", function(err){ console.log("end"); }); }); }); }); });
js想通过一种方案使得以上问题得以解决,得到如下的调用样式。
sayhello("a") .then(value=> { sayhello("b"); }) .then(value=> { sayhello("c"); }) .then(value=> { sayhello("d"); }) .then(value=> { sayhello("e"); }) .then(value=> { sayhello("end"); });
2. 基本用法
Promise的构造函数的参数接受2个参数,都是函数。一个是resolve一个是reject。resolve函数将promise的状态职位fulfilled,reject将promise的状态置为rejected。
这两个函数的具体实现实在then中。如下:
console.log("1111 "); new Promise((resolve,reject)=>{ setTimeout(() => { if(true) resolve("3333 resolved") else reject("3333 rejected"); console.log("2222");//此处的2222,在3333world之前输出。 }, 2000); }) .then(value=>{ console.log(value+':resolved') },value=>{ console.log(value+":rejected"); });
3. 链式调用(返回的是个promise类型)。then中的resolve函数是直接返回的一个非promise的类型,那么这个返回值会作为下一个then的输入参数。
const p = new Promise(function(resolve,reject){ resolve(1); }).then(function(value){ // 第一个then // 1 console.log(value); return value * 2; }).then(function(value){ // 第二个then // 2 console.log(value); }).then(function(value){ // 第三个then // undefined console.log(value); return Promise.resolve('resolve'); }).then(function(value){ // 第四个then // resolve,value是个string类型 console.log(value); return Promise.reject('reject'); }).then(function(value){ // 第五个then //reject:reject,value是个string类型 console.log('resolve:' + value); }, function(err) { console.log('reject:' + err); });
3. 关于2个参数。
then或者promise的构造函数中都可以使用两个参数,resolve函数是必须的,reject函数是可选的。一般不用reject函数,对于错误的处理直接用catch比较简便。catch的用法参考下面。
4.then返回一个promise。
这是promise解决的最重要的“地狱回调”的问题。连环调用querySQL中,用此方法可以使代码可读性增加。返回promise实例,会使得之前的promise被取代。相当于下一个then前新建了一个promise实例。
new Promise( resolve => { setTimeout( () => {//等效于读数据库场景 resolve('hello');//直接运行 }, 2000); }) .then( value => { console.log(value); return new Promise( resolve => { setTimeout( () => { resolve('world'); console.log("---")//---先于world world 输出 }, 2000); }); }) .then( value => { console.log( value + ' world'); });
5. 一个实例(参考慕课网)
console.log('here we go'); new Promise(resolve => { setTimeout( () => { resolve('hello'); }, 2000); }) .then( value => { console.log(value); console.log('everyone'); (function () { return new Promise(resolve => { console.log("---") setTimeout(() => { console.log('Mr.Laurence'); resolve('Merry Xmas');//这里不输出,因为这个promise没有返回,所以他跟下面的then无关。 }, 2000); }); }()); return false; }) .then( value => { console.log(value + ' world'); });
输出为
here we go
hello
everyone
---
false world
Mr.Laurence
6. catch用法。一般简易使用catch来捕获reject函数,不建议使用promise或者then的第二个函数参数。如下:
new Promise( (resolve,reject) => { setTimeout( () => { console.log("111") if(true) { resolve("success"); } else { reject("error");//如果执行了reject,则会直接跳到最后一个catch } }, 200); }) .then( value => { console.log( "222" + value); new Promise( (resolve,reject) => { setTimeout( () => { console.log("333") if(true) { resolve("success"); } else { reject("error"); } }, 200); }) }) .catch( error => { console.log( 'My Error:', error); });
7. catch后面可以继续then,因为catch也会返回一个promise,将catch理解为reject函数即可。
new Promise(resolve => { setTimeout(() => { resolve(); //throw new Error('test error');//这里不能调用throw,这里只能用reject函数 }, 1000); }) .then( () => { console.log('start'); throw new Error('test error'); }) .catch( err => { console.log('I catch:', err.message); // 下面这一行的注释将引发不同的走向 // throw new Error('another error'); }) .then( () => { console.log('arrive here'); }) .then( () => { console.log('... and here'); }) .catch( err => { console.log('No, I catch:', err); });
8.promise.all([])