用 Promise 如何把多个连续的异步过程写得优雅一点?

2016-05-18 17:17:35 +08:00
 SilentDepth

比如说做一件事分几个动作: 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 的理解有问题?

14216 次点击
所在节点    JavaScript
36 条回复
leojoy710
2016-05-18 17:20:10 +08:00
then 可以 return 一个 promise...然后就可以继续 then 了...
SilentDepth
2016-05-18 17:29:24 +08:00
@leojoy710 是可以链式调用,但不能让 step2 的异步完成后再执行 finish
spritevan
2016-05-18 17:30:25 +08:00
leojoy710
2016-05-18 17:32:01 +08:00
@SilentDepth step1.then((d) => step2(d)).then(() => finish())
napsterwu
2016-05-18 17:37:46 +08:00
链式调用的时候,你可以用.catch ,任何一步出错都会跳到最后的 reject ,就和 try catch 一样。然后只要你写的每一个 Promise 都有 return Promise ,就可以链式调用。只是我不知道怎么让第一个 Promise 的数据传递到最后,我就用了 block variable 。
napsterwu
2016-05-18 17:46:34 +08:00
markmx
2016-05-18 17:50:28 +08:00
建议还是使用匿名方法吧..写的太长了...还是不够整齐的.
Promise 你当做一个流程的管理.不要把逻辑代码过多的放进去
bdbai
2016-05-18 18:22:13 +08:00
试试 async/await
SilentDepth
2016-05-18 18:30:40 +08:00
@leojoy710
按这个思路试了一下,结果不符预期:
<script src="https://gist.github.com/SilentDepth/bd558eb503b11f733f97be3322d83b51.js"></script>
step2 也是一个异步动作, finish 需要 step2 完成后才能执行
xqin
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);
});
});

```
magicdawn
2016-05-18 19:02:40 +08:00
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
}
vghdjgh
2016-05-18 19:21:17 +08:00
格式全乱了,看来这里不适合讨论代码。。。
vghdjgh
2016-05-18 19:25:09 +08:00
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
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 方法做这件事
FlowMEMO
2016-05-18 19:57:13 +08:00
赞同 @mcfog . promise 用的爽前提是你的异步函数已经包成 promise 了,而不是每次都在 then()里生成 promise.
otakustay
2016-05-18 20:47:51 +08:00
核心是你先把 setTimeout 改成返回 Promise 的方法,然后就爽了
colatin
2016-05-18 22:03:37 +08:00
Promise.delay
jerray
2016-05-18 23:30:23 +08:00
支持 generator 吗?如果支持可以参考下这篇 https://www.promisejs.org/generators/
还有 co https://github.com/tj/co

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/279595

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX