Javascript 将同步代码和承诺结合起来的正确方法是什么?
我正在编写一个函数,作为实际服务器API调用和userland之间的API中介函数。 此函数用于验证某些输入数据(同步),根据需要对其进行转换(同步),读取某些其他数据(异步)。然后合并结果,并最终用于对服务器端点进行实际调用(异步)。 此函数必须始终返回一个承诺,并且使用它的高级API中的所有执行流都应该通过承诺链来处理 然而,我很难弄清楚我是否用正确的方式来做。另外,我还有一些相关的问题,我找不到答案 这是我迄今为止编写的代码:Javascript 将同步代码和承诺结合起来的正确方法是什么?,javascript,error-handling,async-await,es6-promise,Javascript,Error Handling,Async Await,Es6 Promise,我正在编写一个函数,作为实际服务器API调用和userland之间的API中介函数。 此函数用于验证某些输入数据(同步),根据需要对其进行转换(同步),读取某些其他数据(异步)。然后合并结果,并最终用于对服务器端点进行实际调用(异步)。 此函数必须始终返回一个承诺,并且使用它的高级API中的所有执行流都应该通过承诺链来处理 然而,我很难弄清楚我是否用正确的方式来做。另外,我还有一些相关的问题,我找不到答案 这是我迄今为止编写的代码: import axios from "axios"; impo
import axios from "axios";
import * as MyAPI from "./MyAPI";
import querystring from "querystring";
function fetchFromServerEndpoint(route, routeParams, optQueryParams) {
// Is it ok to use Promise.all() to combine sync and async code as below?
return Promise.all([
new Promise((resolve, reject) => {
const routeReplacedParams = route.replace(/{([^{/]*?)}/g, (match, paramName) => {
const paramValue = routeParams[paramName];
if (paramValue === undefined) {
// Here I need to terminate execution of the replacer function
// and reject this promise
// Is it ok to `throw` here?
throw "UNDEFINED_ROUTE_PARAMETER";
// Can I somehow `return reject(...)` here instead of throw-ing?
}
return paramValue;
});
return resolve(routeReplacedParams);
}),
// Is it fine to use async to mock a promise with synchronous code?
async function(){
return querystring.stringify(optQueryParams);
}),
// This is part of a custom API, returns a legit promise
MyAPI.getAuthorizationAsync(),
]).then(([finalRoute, queryParams, authToken]) => {
// axios library, all APIs will return a promise
return axios.get(`${finalRoute}?${queryParams}`, {
headers: {
Authorization: `Basic ${authToken}`
}
});
});
}
我的问题是:
String.replace
的replace函数中一样,可以在那里“抛出”吗?你能提出更好的选择吗,或者,一个更容易承诺的方法李>
async
将隐式创建承诺,还将隐式转换(未捕获)throw
s为promise.reject()
s并返回intropromise.resolve()
s。这样做的好处是,您可以编写与普通同步函数相同的代码,但它的行为仍然像承诺一样,因此您可以将承诺链接到它。我的这种理解是错误的吗?除了我刚才所描述的以外,还有什么缺点(或优点)需要我注意吗李>
在
try catch
块中使用wait
。这是可读的,不会导致地狱
async function f() {
try {
let response = await fetch('http://no-such-url');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
当出现错误情况时,从同步函数在承诺内停止代码块执行的正确方法是什么?此外,就像String.replace的replace函数一样,可以在那里“抛出”吗?你能提出更好的选择吗,或者,一个更容易承诺的方法
扔在那里没关系。Promise executor函数会捕获executor函数中发生的同步异常,并将其转换为拒绝的承诺
这是否是一件好事取决于你想发生什么。如果您希望调用函数中的承诺被拒绝,那么您可以直接从同步函数中抛出,这将导致异步调用函数中的承诺冒泡,并导致拒绝。否则,您还可以自由地使用常规同步技术,例如从同步函数返回错误条件,检查调用者中的错误,然后采取相应的行动。因此,与任何其他设计一样,它完全取决于您希望如何调用同步函数,以及调用者应该如何检查错误或处理错误。有时返回的错误代码是正确的,有时抛出异常是正确的
例如,在此示例中,如果调用可能抛出()的随机同步函数,则调用方承诺将自动被拒绝,并且将命中.catch()
处理程序
function randomSyncFunctionThatMightThrow() {
// other logic
throw new Error("random error");
}
someFuncThatReturnsPromise().then(arg => {
// bunch of logic here
randomSyncFunctionThatMightThrow();
// other logic here
return someThing;
}).then(result => {
console.log(finalResult);
}).catch(err => {
console.log(err);
});
将函数标记为async将隐式创建一个承诺,也将隐式转换(未捕获)抛出到promise.reject()中,并返回intro promise.resolve()s。这样做的好处是,您可以编写与普通同步函数相同的代码,但它的行为仍然像承诺一样,因此您可以将承诺链接到它。我的这种理解是错误的吗
听起来不错async
函数有一个内置的try/catch
函数,可以自动捕获任何异常并将其转换为拒绝的承诺
除了我刚才所描述的以外,还有什么缺点(或优点)需要我注意吗
强制所有同步代码使用promises异步处理正常返回结果有其局限性。由于明显的编码复杂性原因,您不会将它用于整个程序中的每个同步函数。异步代码的编写和测试要花费更多的时间,所以我不会去寻找让普通的同步东西开始返回承诺并让所有调用方异步处理的方法。有时,如果同步代码也涉及承诺,则某些同步代码更容易与异步代码混合。但是,这不是同步代码的一般用法
我使用Promise.all()来组合函数执行的多个独立阶段的结果。我想这很好,但是有没有其他方法来处理这种情况呢?或者这是一个坏习惯?相反,我是否应该连锁承诺,并将结果从一个承诺传递到下一个承诺,直到我达到需要使用所有承诺的水平
Promise.all()
适用于希望同时有多个独立和并行异步执行链所有在飞的情况下,您只想知道它们何时完成,并立即获得所有结果。承诺链是指当您出于各种原因希望将事情一个接一个地排序时,但通常是因为步骤2取决于步骤1的结果。您应该根据异步代码的需要选择Promise.all()
vs.chaining(并行执行vs.顺序执行)
将同步代码放入一个Promise.all()
可以很好地工作(您甚至不必将它包装成一个Promise作为Promise.all()
将接受运行
async function fetchFromServerEndpoint(route, routeParams, optQueryParams) {
const finalRoute = route.replace(/{([^{/]*?)}/g, (match, paramName) => {
const paramValue = routeParams[paramName];
if (paramValue === undefined) {
// Here I need to abort execution of the replacer function
// parent promise will be rejected
throw new Error("UNDEFINED_ROUTE_PARAMETER");
}
return paramValue;
});
const queryParms = querystring.stringify(optQueryParams);
const authToken = await MyAPI.getAuthorizationAsync();
return axios.get(`${finalRoute}?${queryParams}`, {
headers: {
Authorization: `Basic ${authToken}`
}
});
}
return MyAPI.getAuthorizationAsync().then(authToken => {
return axios.get(`${finalRoute}?${queryParams}`, {
headers: {
Authorization: `Basic ${authToken}`
}
});
});