一个常见的需求是背靠背地执行两个或多个异步操作,其中每个后续操作都是在前一个操作成功时开始的,并且是前一步的结果。在过去,连续执行几个异步操作将导致经典的回调地狱:
doSomething(函数(结果){doSomethingElse(结果,函数(newResult){doThirdThing(newResult,函数(finalResult){log(`得到最终结果:${finalResult}`);},failureCallback);},failureCallback);},failureCallback);
通过承诺,我们通过创建承诺链来实现这一点。promise的API设计使得这一点非常棒,因为回调被附加到返回的promise对象,而不是传递到函数中。
神奇之处在于:然后()
函数返回重新允诺,与原件不同:
const promise=doSomething();const promise2=promise.then(成功回调,失败回调);
第二个承诺(承诺2
)代表的不仅仅是做某事()
,但也属于成功回拨
或故障回拨
您传入了-可以是其他返回承诺的异步函数。在这种情况下,任何回调都会添加到承诺2
排队等待任何一方返回的承诺成功回拨
或故障回拨
.
注:如果希望使用工作示例,可以使用以下模板创建任何返回承诺的函数:
函数doSomething(){return new Promise((resolve)=>{setTimeout(()=>{//完成承诺前要做的其他事情console.log(“做了某事”);//承诺的履行价值解决(“https://example.com/");}, 200);});}
该实现在围绕旧回调API创建Promise第节。
使用此模式,您可以创建更长的处理链,其中每个承诺表示链中一个异步步骤的完成。此外然后
是可选的,并且catch(故障回调)
是的缩写然后(null,failureCallback)
-因此,如果所有步骤的错误处理代码都相同,则可以将其附加到链的末端:
做某事().then(函数(结果){return doSomethingElse(结果);}).then(函数(newResult){return doThirdThing(newResult);}).then(函数(finalResult){log(`得到最终结果:${finalResult}`);}).catch(failureCallback);
你可能会看到这一点用箭头功能相反:
做某事().then((结果)=>doSomethingElse(结果)).then((newResult)=>doThirdThing(newRes结果)).then((finalResult)=>{log(`得到最终结果:${finalResult}`);}).catch(故障回调);
注:箭头函数表达式可以有隐性回报; 所以,()=>x
是的缩写()=>{return x;}
.
做些其他事情
和做第三件事
可以返回任何值&如果它们返回承诺,则该承诺将首先等待,直到它完成,而下一个回调将接收实现值,而不是承诺本身。务必回报来自然后
回调,即使承诺总是决定未定义
。如果之前的处理者开始承诺但没有返回,则无法再跟踪其结算,并且承诺被称为“浮动”。
做某事().then((url)=>{//fetch(url)前面缺少“return”关键字。获取(url);})然后(结果)=>{//结果未定义,因为上一个//处理程序。无法知道fetch()的返回值//再打电话,或者它是否成功。});
通过返回取来
调用(这是一个承诺),我们既可以跟踪它的完成情况,也可以在它完成时接收它的值。
做某事().then((url)=>{//添加了`return`关键字返回获取(url);})然后(结果)=>{//结果是一个Response对象});
如果您有竞态条件,浮动承诺可能会更糟-如果最后一个处理程序的承诺没有返回,则下一个处理函数的承诺然后
处理程序将被提前调用,它读取的任何值都可能不完整。
const listOfIngredients=[];做某事().then((url)=>{//fetch(url)前面缺少“return”关键字。获取(url).then((res)=>res.json())然后((数据)=>{listOfIndients.push(数据);});})然后(()=>{console.log(成分列表);//listOfIngredients将始终为[],因为获取请求尚未完成。});
因此,根据经验法则,每当您的操作遇到承诺时,请将其退回并推迟到下一次处理然后
处理程序。
const listOfIngredients=[];做某事().then((url)=>{//“return”关键字现在包含在fetch调用之前。返回获取(url).then((res)=>res.json())然后((数据)=>{listOfIndients.push(数据);});})然后(()=>{console.log(成分列表);//listOfIndients现在将包含来自fetch调用的数据。});
更好的是,您可以将嵌套链展平为单个链,这样更简单,也更容易处理错误。有关详细信息,请参阅嵌套第节。
做某事().then((url)=>获取.then((res)=>res.json())然后((数据)=>{listOfIndients.push(数据);})然后(()=>{console.log(成分列表);});
使用异步
/等待
可以帮助您编写更直观且类似于同步代码的代码。下面是使用异步
/等待
:
异步函数logIngredients(){const url=等待doSomething();const res=等待获取(url);const数据=等待res.json();listOfIngredients.push(数据);console.log(成分列表);}
请注意,除了等待
承诺前的关键词。唯一的折衷之一是很容易忘记等待
关键字,只有在类型不匹配时才能修复(例如,尝试使用promise作为值)。
异步
/等待
建立在承诺之上——例如,做某事()
是与以前相同的函数,因此只需进行最少的重构即可将承诺更改为异步
/等待
。您可以阅读有关异步
/等待
中的语法异步函数和等待
参考文献。
注: 异步
/等待
与普通承诺链具有相同的并发语义。等待
在一个异步函数中,不会停止整个程序,只会停止依赖于其值的部分,因此在等待
正在挂起。