比如说做一件事分几个动作: step1 、 step2 、 finish ,前两个是异步的, step1 完成后才能执行 step2 (然后 finish )。我想到用 Promise 这么写:
let prom = new Promise((resolve, reject) => { // step1 ,用 setTimeout 模拟一下异步 setTimeout(() => resolve('1'), 1000); // 先忽略 reject 相关动作 }); prom.then(data => { // step2 setTimeout(() => { prom.then(data => { // finish console.log(`完成: ${data}`) }); }, 1000); });
可这样不就成 callback hell 了吗?而且从 step2 往后传数据也不方便。
由此引发的还有 step1 出错执行reject()
如何中止整个过程的问题。
还是我对 Promise 的理解有问题?
![]() | 1 leojoy710 2016-05-18 17:20:10 +08:00 ![]() then 可以 return 一个 promise...然后就可以继续 then 了... |
2 SilentDepth OP @leojoy710 是可以链式调用,但不能让 step2 的异步完成后再执行 finish |
![]() | 3 spritevan 2016-05-18 17:30:25 +08:00 |
![]() | 4 leojoy710 2016-05-18 17:32:01 +08:00 @SilentDepth step1.then((d) => step2(d)).then(() => finish()) |
![]() | 5 orcsfox 2016-05-18 17:37:46 +08:00 链式调用的时候,你可以用.catch ,任何一步出错都会跳到最后的 reject ,就和 try catch 一样。然后只要你写的每一个 Promise 都有 return Promise ,就可以链式调用。只是我不知道怎么让第一个 Promise 的数据传递到最后,我就用了 block variable 。 |
![]() | 6 orcusfox 2016-05-18 17:46:34 +08:00 |
![]() | 7 markmx 2016-05-18 17:50:28 +08:00 建议还是使用匿名方法吧..写的太长了...还是不够整齐的. Promise 你当做一个流程的管理.不要把逻辑代码过多的放进去 |
![]() | 8 bdbai 2016-05-18 18:22:13 +08:00 via Android 试试 async/await |
9 SilentDepth OP @leojoy710 按这个思路试了一下,结果不符预期: <script src="https://gist.github.com/SilentDepth/bd558eb503b11f733f97be3322d83b51.js"></script> step2 也是一个异步动作, finish 需要 step2 完成后才能执行 |
![]() | 10 O3YwA1ENkb7i35XJ 2016-05-18 18:50:11 +08:00 ``` new Promise((resolve, reject) => { //step 1 setTimeout(() => resolve('1'), 1000); }).then(data => { return new Promise((resolve, reject) => { //step 2 setTimeout(() => resolve([data, '2']), 1000); }).then(data => { //finish console.log('finish', data); }); }); ``` |
![]() | 11 magicdawn 2016-05-18 19:02:40 +08:00 |
![]() | 12 vghdjgh 2016-05-18 19:20:20 +08:00 function step1Async() { return new Promise((resolve, reject) => { step1((error, result) => { if (error) { reject(error); } else { resolve(result); } }); }); } ... try{ const result = await step1Async(); await step2Async(); await finishAsync(); } catch(error){ // error } |
![]() | 13 vghdjgh 2016-05-18 19:21:17 +08:00 格式全乱了,看来这里不适合讨论代码。。。 |
![]() | 14 vghdjgh 2016-05-18 19:25:09 +08:00 <script src="https://gist.github.com/plantain-00/ec1a8c6a8ea857ce53d3c066949d60b2.js"></script> |
![]() | 15 xjp 2016-05-18 19:29:18 +08:00 我一般不喜欢使用 Promise 构造函数 我一般这么用的 ``` let prom = new Promise(); prom.then(data => { // step 2 let prom1 = new Promise(); setTimeout(() => prom1.resolve('2'), 1000); return prom1; }).then(data =>{ // step 3 return "3"; }).then(data =>{ // finish console.log('finish', data); }); // step 1 setTimeout(() => prom.resolve('1'), 1000); ``` 如果 then 里 return 的值是 promise 则将 resolve 的结果传入下一个 then 如果 then 里 return 返回的不是 promise 则将结果直接传入下一个 then |
![]() | 16 mcfog 2016-05-18 19:43:24 +08:00 ![]() 这里的陷阱在于你的 setTimeout 是 callback 风格的,不是 promise 风格的 一定要那 setTimeout 打比方(或者比如外部库是 callback 风格)的话,一定要先转换成 promise 风格 ``` function step1() { console.log('step1 start'); return new Promise(resolve => {setTimeout( ()=> {resolve('step1 result');}, 1000)}); } function step2(data) { console.log('step2, param is', data); return new Promise(resolve => {setTimeout( ()=> {resolve('step2 result');}, 1000)}); } //正常的 promise 串行代码如下 step1() .then(step2) .then(result => { console.log('finish, param is', result) }); ``` bluebird 等三方 promise 一般都提供标准的 promisify 方法做这件事 |
17 FlowMEMO 2016-05-18 19:57:13 +08:00 赞同 @mcfog . promise 用的爽前提是你的异步函数已经包成 promise 了,而不是每次都在 then()里生成 promise. |
18 otakustay 2016-05-18 20:47:51 +08:00 via iPad 核心是你先把 setTimeout 改成返回 Promise 的方法,然后就爽了 |
19 colatin 2016-05-18 22:03:37 +08:00 Promise.delay |
![]() | 20 jerray 2016-05-18 23:30:23 +08:00 |
![]() | 21 leojoy710 2016-05-19 08:16:50 +08:00 @SilentDepth 原来是我说的太简略...请看 @mcfog 的回复... |
22 hging 2016-05-19 08:51:12 +08:00 via iPhone async 大法好 |
23 SilentDepth OP 感谢 @napsterwu @xjp @mcfog 我发现我的问题在于不知道`then()`的参数返回一个 Promise 会并入外部的 Promise composition ( MDN 没有写啊……),看来还是我对 Promise 的理解不到位。 现在的问题就是中间的`catch()`如何优雅地中止掉整个 composition 。调用`reject()`后会直接跳到后面最近的 rejection handler ,然后就继续走正常路线了。不是应该有个`Promise.prototype.cancel()`之类的方法吗? to @xjp : 另外,我说的是 ES2015 里的 Promise ,构造函数的参数必须要有,这个怪我一开始没说清。 |
![]() | 24 orcusfox 2016-05-19 09:43:19 +08:00 @SilentDepth Promise.all() |
25 hantsy 2016-05-19 10:13:18 +08:00 用 RxJS 代替。 |
26 SilentDepth OP @napsterwu `Promise.all()`就不是分步了呀 |
![]() | 27 orcusfox 2016-05-19 13:57:32 +08:00 @SilentDepth 我上面不是还发了个链接演示怎么分步么 |
![]() | 28 orcusfox 2016-05-19 13:58:15 +08:00 |
![]() | 29 orcusfox 2016-05-19 13:58:58 +08:00 @SilentDepth 抱歉没搞好,左上角要选择 ES6 |
30 SilentDepth OP @napsterwu 我是说「 step2 的异步成功后执行 finish 」这个需求用`Promise.all()`不能实现 |
31 SilentDepth OP 通过空 Promise 实现了一个中止过程功能: https://gist.github.com/SilentDepth/67866e565946e84b1409d16d294b6181 但感觉这样不优雅。 Any idea? |
![]() | 32 yolio2003 2016-05-19 19:47:16 +08:00 各种大神回复了,楼主还是没明白。。。 |
![]() | 33 yolio2003 2016-05-19 22:53:59 +08:00 所以我觉得其实是 promise 本身的问题 (逃 |
![]() | 34 jimliang 2016-05-20 00:08:16 +08:00 楼主对 Promise 根本不熟练,其次 step1 和 step2 的逻辑就是一样的,可以封装在一起。两行搞定 ``` const step = data=>new Promise(resolve=>setTimeout(resolve, 1000, data)); step('1').then(step).then(data=> console.log(`完成: ${data}`)); ``` |
35 SilentDepth OP @jimliang 刚学。如果是需要各个动作可以独立工作呢? |
![]() | 36 jimliang 2016-05-20 09:35:30 +08:00 @SilentDepth 如果是独立的就另外处理。 Promise 的重点是形成 Promise 链, then 的函数要返回具体的对象或者 Promise 作为下一个传递。我看你把一个 Promise then 了两次,也就是说 resolve 后两个 then 都会执行并不会顺着一条链执行了。 |