问题
现在有多个请求,第一个请求结束后,失败就结束,成功就继续执行第二个请求,第二个请求结束后,失败就结束,成功就继续执行第三个请求,……,这样一直下去,可能会有三个请求,或者五个请求,或者更多。
原有的解决方法
按照以往的思路,会写出如下的嵌套代码:
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
| a(function(res) { if (res) { console.log('a执行成功'); b(function(res) { if (res) { console.log('b执行成功'); c(function(res) { if (res) { console.log('c执行成功'); } else { console.log('c执行失败,结束'); } }); } else { console.log('b执行失败,结束'); } }); } else { console.log('a执行失败,结束'); } });
function a(callback) { callback(true); }
function b(callback) { callback(true); }
function c(callback) { callback(true); }
|
使用Promise解决嵌套回调
如果请求数再多一点,那嵌套会更加严重,可读性很差,这时可以用Promise进行优化,如下:
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 38 39 40 41 42 43 44 45 46 47
| a().then(value => { console.log(value); return b(); }, reason => { console.log(reason); return b(); }).then(value => { console.log(value); return c(); }, reason => { console.log(reason); return c(); }).then(value => { console.log(value); }, reason => { console.log(reason); });
function a() { return new Promise((resolve, reject) => { if (true) { resolve('a执行成功'); } else { reject('a执行失败,结束'); } }) }
function b() { return new Promise((resolve, reject) => { if (true) { resolve('b执行成功'); } else { reject('b执行失败,结束'); } }) }
function c() { return new Promise((resolve, reject) => { if (true) { resolve('c执行成功'); } else { reject('c执行失败,结束'); } }) }
|
每个函数都不再采用callback进行回调,而是改为返回Promise对象,外部就可以在then里面获取到执行结果,然后继续调用下一个函数,还是返回Promise对象,在下一个then里获取到。
这里的关键就是:then里面返回的结果,会在下一个then里拿到,这里所说的结果,包括某个值,或者是(resolve, reject)这样的回调。
如何提前终止Promise链?
上面的代码还是有问题的,如果在中间某个环节出现了reject,它只会导致下一个then执行到reject分支,下下个then又是执行了resolve分支,很明显,我们期望是只要有一个reject了,后续都要执行reject分支。
这个问题的原因是因为在于我们处理了reject分支,但reject里又没有返回新的Promise,导致下个then进入resolve分支,这里解决的方法有两个:
- 改成都返回一个reject状态的Promise对象。
- 直接把前面的reject分支都去掉,最后面的then才处理reject。
如何实现延时有序执行?
如果b()需要延时5s后执行,要怎么写?显然不能写成这样:
1 2 3
| setTimeout(function(){ return b(); }, 5000);
|
setTimeout里面的任务会放到宏任务队列,而Promise的任务会被放到微任务队列,两者之间没有执行顺序约束,而我们是要在b执行后才能执行c,所以并不能实现我们的目的,这里的解决方法,就是要保证setTimeout里面的任务执行结束,才能继续执行接下来的Promise任务,可以引入一个中间Promise实现,定义delay函数:
1 2 3 4 5 6 7
| function delay(delayTime) { return new Promise((resolve, reject) => { setTimeout(function() { resolve(); }, delayTime); }); }
|
这里在调用delay函数后,当超时时间到了,setTimeout里的任务执行了,才会回调resolve,然后才继续执行接下来的then里面的代码。
最终Promise代码
解决了上面两个常见问题,最终的代码如下:
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| hello().then(value => { console.log('成功'); }, reason => { console.log('失败'); });
function hello() { return new Promise((resolve, reject) => { console.log('执行a'); a().then( value => { return delay(2000); }, reason => Promise.reject(reason)).then(value => { console.log('执行b'); return b(); }, reason => Promise.reject(reason)).then(value => { console.log('执行c'); return c(); }, reason => Promise.reject(reason)).then(value => { console.log('执行d'); return d(); }, reason => Promise.reject(reason)).then( resolve, reject ); }); }
function a() { return new Promise((resolve, reject) => { if (true) { resolve('a执行成功'); } else { reject('a执行失败,结束'); } }) }
function b() { return new Promise((resolve, reject) => { if (false) { resolve('b执行成功'); } else { reject('b执行失败,结束'); } }) }
function c() { return new Promise((resolve, reject) => { if (true) { resolve('c执行成功'); } else { reject('c执行失败,结束'); } }) }
function d() { return new Promise((resolve, reject) => { if (true) { resolve('d执行成功'); } else { reject('d执行失败,结束'); } }) }
function delay(delayTime) { return new Promise((resolve, reject) => { setTimeout(function() { resolve(); }, delayTime); }); }
|