使用异步 JavaScript 时,我的代码中的每个其他函数如下所示:
function_name()
{
return new Promise((resolve,reject) => {
// do some async task
resolve();
});
}
即使使用 ES6
async
/await
我也无法避免“return new Promise((resolve,reject) => { ... });
”部分。
是否有另一种方法可以编写这样的代码而不需要所有这些重复的行?
首先,您应该避免“promise 反模式”,它将新的 Promise 包装在已经返回 Promise 的其他函数周围。如果你这样做,那么你可以完全停止这样做,只返回异步操作已经创建的承诺。这将立即简化事情。 如果您有未承诺的旧式基于回调的操作,那么一般来说,您应该仅承诺一次基本异步函数,然后仅使用返回承诺的函数版本进行编码。然后,您的所有逻辑流程都使用 Promise,您将不会在代码的常规流程中看到
new Promise(...)
样板。
此外,现在您通常可以避免手动承诺。如果您使用的是 Node.js,那么util.promisify()
可用于创建遵循 Node.js 调用约定的任何异步函数的承诺版本。
或者,如果您使用 Bluebird Promise 库,您可以使用Promise.promisify()
承诺单个函数,也可以使用
Promise.promisifyAll()
承诺整个接口。使用任何这些替代方法都可以避免您似乎反对的样板。
但是,如果您要自己从头开始手动 Promisify 每个函数并且不使用任何其他辅助函数,那么就无法避免
new Promise(...)
语法。这就是所需要的。
因此,您的大部分编码不应涉及new Promise(...)
。如果是这样,那么您需要向我们展示一些示例,我们可以建议更好/不同的编码方式。
你应该如何编写下面的简单函数 test() { return new Promise((resolve,reject) => { setTimeout(() => {resolve(42); }); }); }这看起来像是一个立即解析为 42 的承诺,这样你就可以:
const test = _=>Promise.resole(42);
如果您想要一个在特定时间解决的 Promise 并多次使用它,您可以编写以下内容:
const later = time => value =>
new Promise(
(resolve,reject)=>
_=>resolve(value),
time
);
const afterOneSecond = later(1000);
afterOneSecond("resolves after one second");
如果您想稍后拒绝某些内容:
later(1000)(Promise.reject("rejects after one second"));
如果您正在使用真实的承诺而不是嘲笑的承诺进行测试,并且需要传递被拒绝的承诺,而控制台不会警告您并点击“未捕获”断点,您可以这样做:
const p = Promise.reject("rejects after one second");
p.catch(ignore=>ignore);//catch it
later(1000)(p);//pass promise without catch to later
如果你有一个函数可以返回某件事的承诺并处理该值,你就可以做到。
myFunction()
.then(
something=>something.data
)
.then(
data=>...
)
如果你想检查something.data是否不为空,如果是你想拒绝你可以这样做:
myFunction()
.then(
something=>
(something&&something.data.length!==0)
? something.data
: Promise.reject("Data cannot be empty")
)
.then(
data=>...
)
.catch(
e=>
(e==="Data cannot be empty")
? "do something special"
: Promse.reject(e)//keep rejecting, other error
);
如果您有一个可以抛出异常的同步函数,它是您的 Promise 链中的第一个函数,并且您希望它抛出的内容最终成为被拒绝的 Promise,您可以执行以下操作:
const syncFunctionThatThrows = arg => {
if(arg===1){
throw "arg cannot be 1";
}
return arg;
};
//starting promise chain with synchronous function that can throw
// if it throws the error is absorbed by the chain and produces
// a rejected promise
Promise.resolve(1)//going to pass 1 to syncFunctionThatThrows
.then(syncFunctionThatThrows);
要使用回调 api 作为承诺,你可以这样做:
const asPromise = object => fn => args =>
new Promise(
(resolve,reject)=>
fn.apply(
object,
args.concat([
(...result)=>
(result[0])//first argument of callback is error
? reject(result[0])//reject with error
: resolve(result.slice(1,result.length))//resolve with result(s)
])
)
);
const callbackApiObjectAsPromise = asPromis(callbackApi);
callbackApiObjectAsPromise(callbackApi.someMethod)(["arg1","arg2"])
.then(
result=>...
)
//example of mysqljs
const connectionAsPromise = asPromise(connection);
connectionAsPromise(connection.query)([
'SELECT * FROM `books` WHERE `author` = ?',
['David']
]).then(
([results, fields])=>...
);
如果你不喜欢 DIY,你可以按照 Pointy 的建议使用promisifyAll
或 promisify。
global.res= function (v){
return new Promise(function(r){r(v);});
}
global.rej= function (v){
return new Promise(function(r,rr){rr(v);});
}
然后您可以在代码中使用 res/rej,如下所示:
async function testing(param){
return param < 10 ? res(true) : rej(false)
}
console.log(await testing(2)) // shows 'true'
console.log(await testing(20)) // throws error with 'false' as value