根据您的描述,您的函数似乎没有返回 Promises。相反,它们通过回调(您在其中编写 console.log)处理异步行为。没有承诺 async/await 什么都不做。您的代码没有抛出任何错误的原因是 await 旨在静默忽略任何不是承诺的内容,以支持可能返回或不返回 Promise 的函数(例如,在某些情况下,函数返回 Promise 但在其他情况下,它返回null)
为了按顺序执行您的功能,您需要等待每个功能完成。我们知道他们完成的一个地方是在他们的回调中。所以你需要重写你的代码如下:
function getData() {
api.getBaseInfo(name, num, (error, response) => {
console.log(response);
api.getMainInfo(address, company, { type:'init' , family: name, id: num }, (error, response) => {
console.log(response);
api.getBackData(num, (error, orders, genre) => {
console.log(orders)
});
});
});
}
getData();
这样每个函数都可以在执行下一个函数之前完成。
承诺
虽然上述代码有效,但更复杂的代码会产生非常深的嵌套。嵌套可以通过使用命名函数而不是匿名函数来解决。事实上,在传统编程(例如 C++)中,这是鼓励的——你应该重构你的代码,使每个函数都尽可能短:
function baseInfoHandler(error, response) {
console.log(response);
const query = { type:'init' , family: name, id: num };
api.getMainInfo(address, company, query, mainInfoHandler);
}
function mainInfoHandler(error, response) {
console.log(response);
api.getBackData(num, backDataHandler);
}
function backDataHandler(error, orders, genre) {
console.log(orders);
}
function getData() {
api.getBaseInfo(name, num, baseInfoHandler);
}
getData();
上面的代码扁平化了回调的嵌套。有些人(尤其是老前辈)可能会争辩说,这段代码比使用匿名函数的代码写得更好。但是,进行这种重构也有缺点。
第一个缺点是一个非常小的缺点,基本上可以忽略:有些人就是不喜欢声明额外的函数或变量。使用匿名函数意味着我们不需要考虑函数名。但这只是口味问题,就像我提到的,可以完全忽略。
第二个缺点更为关键。如果您的代码使用闭包,则无法像上面那样展平代码。或者至少如果您依赖闭包,则实施起来会更加困难。因此,我们需要一种嵌套范围(维护闭包)的方法,但仍要避免像我给出的第一个示例那样的深度嵌套。
随之而来的是设计模式。一群才华横溢的程序员开始在各种论坛和博客文章中讨论如何将异步回调封装在一个可以返回的对象中。然后,函数的调用者可以使用所述对象以他们喜欢的任何方式处理异步行为。这种设计模式的名字叫做 Promise,在 javascript 发布 Promise 的默认实现之前,有几个库实现了这种设计模式,通常具有不同的特性,但都以一种方式相互兼容:它们都有一个 .then() 方法,您可以将回调传递给它。
注意:这就是 Promise 设计模式所做的一切。与其编写一个接受回调的函数,不如编写一个返回 Promise 的函数。 Promise 有一个接受回调的.then() 方法。
代码仍然是基于回调的,但现在我们可以通过扁平化回调嵌套来使代码更整洁。首先,我们需要将您的函数转换为 Promise:
function promiseBaseInfo (name,num) {
return new Promise((resolve, reject) => {
api.getBaseInfo(name, num, (error, response) => {
if (error) {
reject(error);
}
else {
resolve(response);
}
});
});
}
function promiseMainInfo(address, company, query) {
return new Promise((resolve, reject) => {
api.getMainInfo(address, company, query, (error, response) => {
if (error) {
reject(error);
}
else {
resolve(response);
}
});
});
}
function promiseBackData(num) {
return new Promise((resolve, reject) => {
api.getBackData(num, (error, orders, genre) => {
if (error) {
reject(error);
}
else {
resolve({
orders: orders,
genre: genre
});
}
});
});
}
现在我们有了你的函数的 Promisified 版本,我们可以像这样使用它们:
function getData() {
promiseBaseInfo(name, num)
.then(response => {
console.log(response);
return promiseMainInfo(address, company, { type:'init' , family: name, id: num });
})
.then(response => {
console.log(response);
return promiseBackData(num);
})
.then(response => {
console.log(response.orders);
});
}
getData();
如您所见,.then() 都处于同一嵌套级别。
异步/等待
虽然 Promises 在简化异步代码方面有很大帮助,但它们仍然是基于回调的。此外,执行诸如循环一堆异步任务之类的事情很复杂,因为您需要编写一种递归函数来执行此操作(这使我们回到了回调领域)。
ECMAScript 版本 6 引入了一种新语法来处理 Promise:async 和 await 关键字。
它所做的只是编译这样编写的代码:
async function foo () {
let x = await bar();
console.log(x);
}
进入这个:
function foo () {
return bar().then(x => console.log(x));
}
从解释器的角度来看,Javascript 没有添加任何新内容。这些仍然是 Promise。然而,编译器现在处理如何将您的代码正确地构造成 Promise。
使用async/await,我们可以进一步简化您的代码:
async function getData() {
let response1 = await promiseBaseInfo(name, num);
console.log(response1);
let response2 = await promiseMainInfo(address, company, { type:'init' , family: name, id: num });
console.log(response2);
let response3 = await promiseBackData(num);
console.log(response3.orders);
}
getData();
请注意,Promise 和 async/await 都不是使您的代码按照您想要的方式工作的必要条件。而且它们不会改变您的代码的行为 - getData() 函数仍然是异步的(不是同步的)并且会在它记录所有响应之前返回。它只是使代码更易于阅读。然而,在幕后仍然有一个回调被调度并在以后执行,只有async/await 我们让编译器编写回调,我们只是以线性方式编写代码。请务必记住,即使使用 async/await,代码仍然不同步。
另请注意,我们仍然无法使用您原来的 api... 函数。我们需要将它们转换为 Promisified 版本。