今天给大家推荐几种写JavaScript异步代码的推荐做法。每个场景都有对应的eslint规则,你可以选择配置。no-async-promise-executor不建议将异步函数传递给新Promise的构造函数。//:x:newPromise(async(resolve,reject)=>{});//:white_check_mark:newPromise((resolve,reject)=>{});首先,您在Promise构造函数中异步使用它,然后可能不需要包装Promise。另外,如果async函数抛出异常,新构造的promise实例不会reject,错误也不会被捕获。no-await-in-loop不推荐在循环中使用await。这种写法通常意味着程序没有充分利用JavaScript的事件驱动。//:x:for(consturlofurls){constresponse=awaitfetch(url);}建议将这些异步任务改成并发执行,可以大大提高代码的执行效率。//:white_check_mark:constresponses=[];for(consturlofurls){constresponse=fetch(url);responses.push(response);}awaitPromise.all(responses);no-promise-executor-return不建议在Promise构造函数中返回值。Promise构造函数中返回的值是无用的,返回值不会影响Promise的状态。//:x:newPromise((resolve,reject)=>{returnresult;});正常的做法是把返回值传给resolve,出错了就传给reject。//:white_check_mark:newPromise((resolve,reject)=>{resolve(result);});require-atomic-updates不建议将assignment和await结合使用,可能会造成条件竞争。查看下面的代码,您认为totalPosts的最终值是多少?//:x:lettotalPosts=0;asyncfunctiongetPosts(userId){constusers=[{id:1,posts:5},{id:2,posts:3}];等待睡眠(Math.random()*1000);returnusers.find((user)=>user.id===userId).posts;}asyncfunctionaddPosts(userId){totalPosts+=awaitgetPosts(userId);}awaitPromise.all([addPosts(1),添加帖子(2)]);console.log('帖子数:',totalPosts);totalPosts会打印3或5,而不是8,你可以在浏览器中自己试试。问题是读取totalPosts和更新totalPosts之间存在时间间隔。这会导致竞争条件,即当在单独的函数调用中更新值时,更新不会反映在当前函数范围内。因此,这两个函数都会将它们的结果添加到totalPosts的初始值0中。避免竞争条正确的做法://:white_check_mark:lettotalPosts=0;asyncfunctiongetPosts(userId){constusers=[{id:1,posts:5},{id:2,posts:3}];等待睡眠(Math.random()*1000);returnusers.find((user)=>user.id===userId).posts;}asyncfunctionaddPosts(userId){constposts=awaitgetPosts(userId);总帖子数+=帖子数;//变量被读取并立即更新}awaitPromise.all([addPosts(1),addPosts(2)]);console.log('Postcount:',totalPosts);max-nested-callbacks防止回调监狱,避免大量的深度封装:/*eslintmax-nested-callbacks:["error",3]*///:x:async1((err,result1)=>{async2(result1,(err,result2)=>{async3(result2,(err,result3)=>{async4(result3,(err,result4)=>{console.log(result4);});});});});//:white_check_mark:constresult1=awaitasyncPromise1();constresult2=awaitasyncPromise2(result1);constresult3=awaitasyncPromise3(result2);constresult4=awaitasyncPromise4(result3);console.log(结果4);回调地狱使代码难以阅读和维护。建议将回调重构为Promise并使用现代异步/等待语法no-return-await。返回异步结果时不需要写await。如果你想要等待一个Promise然后立即返回它可能是不必要的。//:x:async()=>{returnawaitgetUser(userId);}所有从异步函数返回的值都被包裹在一个Promise中,你可以直接返回。//:white_check_mark:async()=>{returngetUser(userId);}当然也有例外。如果外面有try...catch包,删除await是捕获不到异常的。在这种情况下,建议为阐明意图,将结果分配给不同行上的变量。//:-1:async()=>{try{returnawaitgetUser(userId);}catch(error){//处理getUser错误}}//:+1:async()=>{try{constuser=awaitgetUser(userId);返回用户;}catch(error){//处理getUsererror}}prefer-promise-reject-errors建议在拒绝Promise时强制使用Error对象,这样更容易跟踪错误堆栈。//:x:Promise.reject('发生错误');//:white_check_mark:Promise.reject(newError('发生错误'));node/handle-callback-err在Node.js回调中强制执行异步异常处理。//:x:functioncallback(err,data){console.log(data);}//:white_check_mark:functioncallback(err,data){if(err){console.log(err);返回;}console.log(data);}在Node.js中,通常将异常作为第一个参数传递给回调函数。忘记处理这些异常可能会导致您的应用程序出现不可预知的问题。该规则只有在函数的第一个参数名为err时才会触发,也可以去.eslintrc文件中自定义异常参数名。node/no-sync反对在存在异步替代方法的Node.js核心API中使用同步方法。//:x:constfile=fs.readFileSync(path);//:white_check_mark:constfile=awaitfs.readFile(path);在Node.js中对I/O操作使用同步方法会阻塞事件循环。在大多数情况下,执行I/O操作时最好使用异步方法。@typescript-eslint/await-thenable不建议等待非Promise函数或值。//:x:functiongetValue(){returnsomeValue;}awaitgetValue();//:white_check_mark:asyncfunctiongetValue(){returnsomeValue;}awaitgetValue();@typescript-eslint/no-floating-promises建议Promise附加异常处理代码。//:x:myPromise().then(()=>{});//:white_check_mark:myPromise().then(()=>{}).catch(()=>{});好习惯,常做异常处理!@typescript-eslint/no-misused-promises不建议将Promises传递到不打算处理它们的地方,例如if条件。//:x:if(getUserFromDB()){}//:white_check_mark::-1:if(awaitgetUserFromDB()){}建议提取一个变量,提高代码的可读性。//:white_check_mark::+1:constuser=awaitgetUserFromDB();if(user){}
