轮询 Async 微信支付 异步递归

轮询 Async 微信支付 异步递归

背景

最近在做小程序,需要实现微信支付的功能

支付完成之后,微信平台那边会触发后端回调,后端才会将数据更新入库

这个过程对后端是异步的,所以需要轮询订单状态,等到状态更新了再刷新数据

理想状态

如果我们要轮询 3 次,间隔 2s,最理想的使用方式应该是:

1
2
3
4
5
6
7
8
9
10
function whatever() {
const status = checkStatus();
if (status) {
// some code
}
}

function checkStatus(count = 2) {
request();
}

这才是符合阅读的使用逻辑,为了实现这种形式的代码,我们可以试试用三种方式来实现

然后再看看最终的效果

推荐方式:Async 模式

想使用 await 接住 async 的值,其 async 必须 有 return。不然默认 返回 undefined

在递归中的 async 递归本身时   一定要用 return await asyncFn 递归本身,不然最后出口就算有 return 也会返回上次调用的 undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
async function checkStatus(count = 2) {
const res = await request();
if (res) {
// 成功了
return true;
} else if (count) {
// 没有成功,进入轮询,每3秒递归发请求
await new Promise((resolve) => setTimeout(resolve, 3000));
return await checkStatus(--count);
} else {
// 轮询次数用完了 依旧没有成功,return false
return false;
}
}

async function whatever() {
const status = await checkStatus();

if (status) {
// some code
}
}

与同步模式一样,我们定时轮询同样不能直接使用 setTimeout

换句话来说,如果我们想让我们的代码以同步的方式来书写,那么如何解决定时的问题,是一个绕不开的问题

以前没有 Promise,我们只能用 sleep,但现在有了 Promise 了,我们可以构造一个需要 2s 才 resolve 的微任务即可,这样,微任务队列就能按照想要的顺序等待执行

最终我们拿到的 status 也就是正确的 status 了

方式一:同步模式

同步模式首先将 checkStatus 函数的请求改为同步请求,然后,然后递归调用:

1
2
3
4
5
6
7
8
9
10
function checkStatus(count = 2) {
const res = request();
if (res) {
return true;
} else if (count) {
setTimeout(() => checkStatus(--count), 2000);
} else {
return false;
}
}

首先我们需要明确,我们肯定肯定不能在 checkStatus 中直接使用 setTimeout,因为进了宏任务,status 肯定为 undefined

所以如果是同步模式,则需要一个 sleep 函数:

1
2
3
4
5
6
7
8
9
10
11
function checkStatus(count = 2) {
const res = request();
if (res) {
return true;
} else if (count) {
sleep(2);
return checkStatus(--count);
} else {
return false;
}
}

这样就实现了我们一开始理想的调用

但是同步请求的问题大家都清楚,所以肯定是不合适的

方式二:回调函数模式

然后我们可以尝试回调函数模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function whatever(status) {
if(status) {
// some code
}
}

function checkStatus(count = 2, cb) {
request({
success(res) {
if (res) {
cb(true)
} else if(count) {
setTimeout(() => checkStatus(--count, cb), 2000)
} else {
cb(false)
}
}
})
}

checkStatus(, whatever)

回调函数的方式我们可以发现,虽然能实现,但违背了我们的理想使用方式

感慨

不开玩笑,当按照这个流程梳理一遍之后,我内心其实有点感慨

可以发现,经过了不停的发展,最终还是回到了”同步”的模样,原因无他,因为同步才是最符合阅读习惯的

而为此引入的标准也好,规范也好,语法糖也好,都是大佬们不满足现状发展出来的

来自小菜鸡的致敬


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!