在一个新的撇()构造函数中使用异步/等待是一种反模式吗?

我正在使用 async.eachLimit函数来控制一次操作的最大数量。

const { eachLimit } = require("async");


function myFunction() {
return new Promise(async (resolve, reject) => {
eachLimit((await getAsyncArray), 500, (item, callback) => {
// do other things that use native promises.
}, (error) => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}

正如您所看到的,我不能将 myFunction函数声明为异步函数,因为我不能访问 eachLimit函数的第二个回调函数中的值。

121876 次浏览

您实际上是在諾构造函数执行器函数中使用了諾,所以这是 承诺构造函数反模式

您的代码是主要风险的一个很好的例子: 不能安全地传播所有错误。

此外,使用 async/await可以使同样的陷阱更加令人惊讶:

let p = new Promise(resolve => {
""(); // TypeError
resolve();
});


(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

与一个天真(错误)的 async等价物:

let p = new Promise(async resolve => {
""(); // TypeError
resolve();
});


(async () => {
await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

在浏览器的网络控制台中查找最后一个。

第一个方法可以工作,因为在一个  种种构造函数执行器函数中的任何 马上异常都可以方便地拒绝新构造的承诺(但是在任何 .then中,你只能靠自己)。

第二种方法不起作用,因为 async函数中的任何即时异常都会拒绝 async函数本身返回的隐式承诺

由于承诺构造函数的执行函数的返回值未被使用,这是一个坏消息!

你的密码

你没有理由不把 myFunction定义为 async:

async function myFunction() {
let array = await getAsyncArray();
return new Promise((resolve, reject) => {
eachLimit(array, 500, (item, callback) => {
// do other things that use native promises.
}, error => {
if (error) return reject(error);
// resolve here passing the next value.
});
});
}

不过,既然你已经有了 await,为什么还要使用过时的并发控制库呢?

static getPosts(){
return new Promise( (resolve, reject) =>{
try {
const res =  axios.get(url);
const data = res.data;
resolve(
data.map(post => ({
...post,
createdAt: new Date(post.createdAt)
}))
)
} catch (err) {
reject(err);
}
})
}

删除等待和异步将解决这个问题。因为您已经应用了承诺对象,这就足够了。

我同意上面给出的答案,但是,有时在承诺中使用异步会更简单,特别是如果您希望连接几个返回承诺的操作并避免 then().then()地狱。我会考虑在这种情况下使用类似的东西:

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)


let p = new Promise((resolve, reject) => {
(async () => {
try {
const op1 = await operation1;
const op2 = await operation2;


if (op2 == null) {
throw new Error('Validation error');
}


const res = op1 + op2;
const result = await publishResult(res);
resolve(result)
} catch (err) {
reject(err)
}
})()
});


(async () => {
await p;
})().catch(e => console.log("Caught: " + e));
  1. 传递给 Promise构造函数的函数不是异步的,所以 linter 不会显示错误。
  2. 可以使用 await按顺序调用所有异步函数。
  3. 可以添加自定义错误来验证异步操作的结果
  4. 错误最终被很好地捕捉到。

不过,缺点是你必须记住把 try/catch和它附加到 reject

相信反模式就是反模式

异步承诺回调中的抛出很容易被捕获。

(async () => {
try {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}
catch (BALL) {
console.log ("(A) BALL CAUGHT", BALL);
throw BALL;
}
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});

或者更简单,

(async () => {
await new Promise (async (FULFILL, BREAK) => {
try {
throw null;
}
catch (BALL) {
BREAK (BALL);
}
});
}) ().
catch (BALL => {
console.log ("(B) BALL CAUGHT", BALL);
});

我没有通过阅读其他答案直接意识到这一点,但重要的是 评估你的异步函数,把它变成一个承诺

因此,如果你定义你的异步函数使用这样的东西:

let f = async () => {
// ... You can use await, try/catch, throw syntax here (see answer of Vladyslav Zavalykhatko) ..
};

你把它变成一个承诺,用:

let myPromise = f()

然后你可以操纵是作为一个承诺,使用例如 Promise.all([myPromise])..。

当然,你可以使用以下方法把它变成 一个班轮:

(async () => { code with await })()