[{"data":1,"prerenderedAt":946},["ShallowReactive",2],{"content:\u002F2021\u002Fpromise-async-await":3,"surround:\u002F2021\u002Fpromise-async-await":935},{"id":4,"title":5,"body":6,"categories":894,"date":896,"description":29,"draft":897,"extension":898,"image":899,"meta":900,"navigation":902,"path":903,"permalink":904,"published":904,"readingTime":905,"recommend":904,"references":910,"seo":926,"sitemap":927,"stem":931,"tags":932,"type":933,"updated":904,"__hash__":934},"content\u002Fposts\u002F2021\u002Fpromise-async-await.md","浅析 Promise、Async\u002FAwait",{"type":7,"value":8,"toc":848},"minimark",[9,14,18,22,33,37,68,72,80,83,89,92,98,113,129,136,142,149,152,158,164,170,180,186,201,208,224,230,237,243,246,253,260,266,268,274,288,295,301,307,312,316,319,325,328,331,351,354,366,369,374,378,381,387,393,400,406,426,429,437,440,448,452,460,486,490,494,497,503,509,513,519,525,532,538,542,555,558,563,575,591,595,598,604,607,613,642,645,651,654,664,679,699,705,709,712,718,721,753,757,760,766,769,775,781,784,797,801,807,814,820,827,833,842],[10,11,13],"h2",{"id":12},"promise","Promise",[15,16,17],"h3",{"id":17},"基本用法",[19,20,21],"p",{},"Promise 的简单封装与使用",[23,24,30],"pre",{"className":25,"code":27,"language":28,"meta":29},[26],"language-js","\u002F\u002F 封装\nfunction 摇色子() {\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      resolve(Math.floor(Math.random() * 6) + 1)\n    }, 3000)\n  })\n}\n\n\u002F\u002F 使用\n摇色子().then(success1, failed1).then(success2, failed2)\n","js","",[31,32,27],"code",{"__ignoreMap":29},[15,34,36],{"id":35},"ma-mi-任务模型","Ma Mi 任务模型",[38,39,40,53,56,59,62,65],"ul",{},[41,42,43,44,48,49,52],"li",{},"Ma 指 ",[45,46,47],"strong",{},"MacroTask","(宏任务)，Mi 指 ",[45,50,51],{},"MicroTask","(微任务)",[41,54,55],{},"先 Ma 再 Mi，即先执行宏任务再执行微任务",[41,57,58],{},"JavaScript 运行时，除了一个正在运行的主线程，引擎还提供一个任务队列（task queue），里面是各种需要当前程序处理的异步任务",[41,60,61],{},"其实最初 JS 只存在一个任务队列，为了让 Promise 回调更早执行，强行又插入了一个异步的任务队列，用来存放 Mi 任务",[41,63,64],{},"宏任务：setTimeout()、setInterval()、 setImmediate()、 I\u002FO、UI渲染（常见的定时器，用户交互事件等等）",[41,66,67],{},"微任务：Promise、process.nextTick、Object.observe、MutationObserver",[15,69,71],{"id":70},"promise-的其他-api","Promise 的其他 API",[73,74,76,79],"h4",{"id":75},"promiseresolveresult-制造一个成功或失败",[31,77,78],{"code":78},"Promise.resolve(result)",": 制造一个成功（或失败）",[19,81,82],{},"制造成功",[23,84,87],{"className":85,"code":86,"language":28,"meta":29},[26],"function 摇色子() {\n  return Promise.resolve(4)\n}\n\n\u002F\u002F 等价于\nfunction 摇色子() {\n  return new Promise((resolve, reject) => {\n    resolve(4)\n  })\n}\n\n摇色子().then(n => console.log(n)) \u002F\u002F 4\n",[31,88,86],{"__ignoreMap":29},[19,90,91],{},"制造失败",[23,93,96],{"className":94,"code":95,"language":28,"meta":29},[26],"function 摇色子() {\n  \u002F\u002F 此处 Promise.resolve 接收的是一个失败的 Promise 实例（状态为 reject）\n  return Promise.resolve(new Promise((resolve, reject) => reject()))\n}\n\n摇色子().then(n => console.log(n)) \u002F\u002F 1 Uncaught (in promise) undefined\n",[31,97,95],{"__ignoreMap":29},[19,99,100,101,104,105,112],{},"关于 ",[31,102,103],{"code":103},"Promise.resolve"," 接收参数的问题，",[106,107,111],"a",{"href":108,"rel":109},"https:\u002F\u002Fes6.ruanyifeng.com\u002F#docs\u002Fpromise#Promise-resolve",[110],"nofollow","ECMAScript 6 入门","里其实说得很清楚",[114,115,116],"blockquote",{},[19,117,118,119,121,122,124,125,128],{},"如果参数是 Promise 实例，那么",[31,120,103],{"code":103},"将不做任何修改、原封不动地返回这个实例；如果参数是一个原始值，或者没有参数，",[31,123,103],{"code":103},"都会直接返回一个",[31,126,127],{"code":127},"resolved","状态的 Promise 对象。",[73,130,132,135],{"id":131},"promiserejectreason-制造一个失败",[31,133,134],{"code":134},"Promise.reject(reason)",": 制造一个失败",[23,137,140],{"className":138,"code":139,"language":28,"meta":29},[26],"Promise.reject('我错了')\n\u002F\u002F 等价于\nnew Promise((resolve, reject) => reject('我错了'))\n\nPromise.reject('我错了').then(null, reason => console.log(reason)) \u002F\u002F 我错了\n",[31,141,139],{"__ignoreMap":29},[73,143,145,148],{"id":144},"promiseall数组-等待全部成功或者有一个失败",[31,146,147],{"code":147},"Promise.all(数组)",": 等待全部成功，或者有一个失败",[19,150,151],{},"全部成功，将所有成功 promise 结果组成的数组返回",[23,153,156],{"className":154,"code":155,"language":28,"meta":29},[26],"Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])\n  .then(values => console.log(values)) \u002F\u002F [1, 2, 3]\n",[31,157,155],{"__ignoreMap":29},[19,159,160,161],{},"只要有一个失败，就结束，",[45,162,163],{},"返回最先被 reject 失败状态的值",[23,165,168],{"className":166,"code":167,"language":28,"meta":29},[26],"Promise.all([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])\n  .then(values => console.log(values)) \u002F\u002F Uncaught (in promise) 1\n",[31,169,167],{"__ignoreMap":29},[19,171,172,175,176,179],{},[31,173,174],{"code":174},"Promse.all"," 在需要",[45,177,178],{},"对多个异步进行处理","时往往非常有用；",[19,181,182,183,185],{},"不过在某些特殊情况下，直接使用",[31,184,174],{"code":174},"就显得不那么方便了",[19,187,188,189,192,193,196,197,200],{},"举个例子，比如现在有 3 个请求，request1, request2 和 request3，我们需要对这 3 个请求进行统一处理，并且不管请求成功还是失败，都需要拿到所有的响应结果，如果这时候使用",[31,190,191],{"code":191},"Promise.all([request1, request2, request3])","的话，request1 请求失败了，后面的两个请求 request2, request3 就都不会执行了（这里实际上是 request1 在 ",[31,194,195],{"code":195},"rejected"," 之后，被 ",[31,198,199],{"code":199},"Promise.all([]).catch"," 给捕获了 ）。",[19,202,203,204,207],{},"如何解决 ",[31,205,206],{"code":206},"Promise.all()"," 在第一个 Promise 失败就会中断的问题？",[19,209,210,211,214,215,217,218,220,221,223],{},"利用 ",[31,212,213],{"code":213},".then()"," 后会返回一个状态为 ",[31,216,127],{"code":127}," 的 Promise（即会自动包装成一个已",[31,219,127],{"code":127},"的promise），从而避免被 ",[31,222,199],{"code":199}," 捕获",[23,225,228],{"className":226,"code":227,"language":28,"meta":29},[26],"\u002F\u002F 3 个请求\nconst request1 = () => new Promise((resolve, reject) => {\n setTimeout(() => {\n   reject('第 1 个请求失败')\n }, 1000)\n})\n\nconst request2 = () => new Promise((resolve, reject) => {\n setTimeout(() => {\n   reject('第 2 个请求失败')\n }, 2000)\n})\n\nconst request3 = () => new Promise((resolve, reject) => {\n setTimeout(() => {\n   resolve('第 3 个请求成功')\n }, 3000)\n})\n\nPromise.all([\n  request1().then(value => ({ status: 'ok', value }), reason => ({ status: 'not ok', reason })),\n  request2().then(value => ({ status: 'ok', value }), reason => ({ status: 'not ok', reason })),\n  request3().then(value => ({ status: 'ok', value }), reason => ({ status: 'not ok', reason }))\n]).then(result => console.log(result))\n",[31,229,227],{"__ignoreMap":29},[19,231,232,233,236],{},"可以把对每个请求的",[31,234,235],{"code":235},".then","操作封装一下",[23,238,241],{"className":239,"code":240,"language":28,"meta":29},[26],"const x = promiseList => promiseList.map(promise => promise.then(value => ({\n  status: 'ok',\n  value\n}), reason => ({\n  status: 'not ok',\n  reason\n})))\n\nconst xxx = promiseList => Promise.all(x(promiseList))\n\nxxx([request1(), request2(), request3()])\n  .then(result => console.log(result))\n",[31,242,240],{"__ignoreMap":29},[19,244,245],{},"打印结果如下：",[19,247,248],{},[249,250],"img",{"alt":251,"src":252},"Promise.all 的执行结果","\u002Fimages\u002Fpromise-async-await\u002Fpromise_01.png",[73,254,256,259],{"id":255},"promiseallsettled数组-等待全部状态改变",[31,257,258],{"code":258},"Promise.allSettled(数组)",": 等待全部状态改变",[23,261,264],{"className":262,"code":263,"language":28,"meta":29},[26],"Promise.allSettled([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])\n  .then(result => console.log(result))\n",[31,265,263],{"__ignoreMap":29},[19,267,245],{},[19,269,270],{},[249,271],{"alt":272,"src":273},"Promise.allSettled 的执行结果","\u002Fimages\u002Fpromise-async-await\u002Fpromise_02.png",[19,275,276,277,280,281,284,285,287],{},"可以看出 ",[31,278,279],{"code":279},"Promise.allSettled"," 的作用其实和上面我们实现的 ",[31,282,283],{"code":283},"xxx"," 函数的作用是一致的，因此针对上文提到场景，可以直接使用 ",[31,286,279],{"code":279},"，更加简洁。",[73,289,291,294],{"id":290},"promiserace数组-等待第一个状态改变",[31,292,293],{"code":293},"Promise.race(数组)",": 等待第一个状态改变",[23,296,299],{"className":297,"code":298,"language":28,"meta":29},[26],"Promise.race([request1(), request2(), request3()]).then((result) => {\n  console.log(result)\n}).catch((error) => {\n  console.log(error) \u002F\u002F 第 1 个请求失败\n})\n",[31,300,298],{"__ignoreMap":29},[19,302,303,306],{},[31,304,305],{"code":305},"Promise.race([request1, request2, request3])","里面哪个请求最先响应，就返回其对应的结果，不管结果本身是成功状态还是失败状态（这里最先响应的请求是 request1）。",[114,308,309],{},[19,310,311],{},"一般情况下用不到 Promise.race 这个 api，不过在某些场景下还是有用的。例如在多台服务器部署了同样的服务端代码，要从一个商品列表的接口拿数据，这时候就可以在 race 中写上所有服务器中的查询商品列表的接口地址，哪个服务器响应快，就优先从哪个服务器拿数据。",[15,313,315],{"id":314},"promise-的应用场景","Promise 的应用场景",[73,317,318],{"id":318},"多次处理一个结果",[23,320,323],{"className":321,"code":322,"language":28,"meta":29},[26],"摇色子().then(v => v1).then(v1 => v2)\n",[31,324,322],{"__ignoreMap":29},[19,326,327],{},"第一个回调函数完成以后，会将返回结果作为参数，传入第二个回调函数。",[73,329,330],{"id":330},"串行",[38,332,333,336,342],{},[41,334,335],{},"这里有一个悖论：一旦 promise 出现，那么任务就已经执行了",[41,337,338,339],{},"所以",[45,340,341],{},"不是 promise 串行，而是任务串行",[41,343,344,345,350],{},"解法：把任务放进队列，完成一个再做下一个（",[106,346,349],{"href":347,"rel":348},"https:\u002F\u002Fsegmentfault.com\u002Fa\u002F1190000016832285",[110],"用 Reduce 实现 Promise 串行执行","）",[73,352,353],{"id":353},"并行",[19,355,356,359,360,359,362,365],{},[31,357,358],{"code":358},"Promise.all","、",[31,361,279],{"code":279},[31,363,364],{"code":364},"Promise.race","都可以看作是并行地在处理任务",[19,367,368],{},"这里可能你会产生疑问，JS 不是单线程吗，怎么做到并行执行任务？",[114,370,371],{},[19,372,373],{},"这里指的是并行地做网络请求的任务，而网络请求实际上是由浏览器来做的，并非是 JS 做的，就像 setTimeout 是浏览器的功能而不是 JS 的，setTimeout 只是浏览器提供给 JS 的一个接口。",[15,375,377],{"id":376},"promise-的错误处理","Promise 的错误处理",[73,379,380],{"id":380},"自身的错误处理",[19,382,383,384,386],{},"promise 自身的错误处理其实挺好用的，直接在",[31,385,235],{"code":235},"的第二个回调参数中进行错误处理即可",[23,388,391],{"className":389,"code":390,"language":28,"meta":29},[26],"promise.then(s1, f1)\n",[31,392,390],{"__ignoreMap":29},[19,394,395,396,399],{},"或者使用",[31,397,398],{"code":398},".catch","语法糖",[23,401,404],{"className":402,"code":403,"language":28,"meta":29},[26],"\u002F\u002F 上面写法的语法糖\npromise.then(s1).catch(f1)\n",[31,405,403],{"__ignoreMap":29},[114,407,408],{},[19,409,410,411,414,415,418,419,422,423,350],{},"建议总是使用",[31,412,413],{"code":413},"catch()","方法，而不使用",[31,416,417],{"code":417},"then()","方法的第二个参数，原因是第二种写法可以捕获前面",[31,420,421],{"code":421},"then","方法执行中的错误，也更接近同步的写法（",[31,424,425],{"code":425},"try\u002Fcatch",[73,427,428],{"id":428},"全局错误处理",[19,430,431,432],{},"以axios为例，",[106,433,436],{"href":434,"rel":435},"https:\u002F\u002Fjuejin.cn\u002Fpost\u002F6844903569745788941",[110],"Axios 作弊表",[73,438,439],{"id":439},"错误处理之后",[38,441,442,445],{},[41,443,444],{},"如果你没有继续抛错，那么错误就不再出现",[41,446,447],{},"如果你继续抛错，那么后续回调就要继续处理错误",[10,449,451],{"id":450},"前端似乎对-promise-不满","前端似乎对 Promise 不满",[19,453,454,459],{},[106,455,458],{"href":456,"rel":457},"https:\u002F\u002Fblog.fundebug.com\u002F2017\u002F04\u002F04\u002Fnodejs-async-await\u002F",[110],"Async\u002FAwait替代Promise的6个理由","，主要是以下 6 个方面：",[38,461,462,465,468,471,474,477],{},[41,463,464],{},"简洁",[41,466,467],{},"错误处理",[41,469,470],{},"条件语句",[41,472,473],{},"中间值",[41,475,476],{},"错误栈",[41,478,479,480,482,483,485],{},"调试（在",[31,481,235],{"code":235},"代码块中设置断点，使用 Step Over 快捷键，调试器不会跳到下一个",[31,484,235],{"code":235},"，因为它只会跳过异步代码）",[10,487,489],{"id":488},"async-await","async \u002F await",[15,491,493],{"id":492},"async-await-基本用法","async \u002F await 基本用法",[19,495,496],{},"最常见的用法",[23,498,501],{"className":499,"code":500,"language":28,"meta":29},[26],"const fn = async() => {\n  const temp = await makePromise()\n  return temp + 1\n}\n",[31,502,500],{"__ignoreMap":29},[19,504,505,506],{},"优点：完全",[45,507,508],{},"没有缩进，就像是在写同步代码",[15,510,512],{"id":511},"封装一个-async-函数","封装一个 async 函数",[19,514,515,518],{},[31,516,517],{"code":517},"async","的封装和使用",[23,520,523],{"className":521,"code":522,"language":28,"meta":29},[26],"function 摇色子() {\n  return new Promise((resolve, reject) => {\n    setTimeout(() => {\n      resolve(Math.floor(Math.random() * 6) + 1)\n    }, 3000)\n  })\n}\n\nasync function fn() {\n  const result = await 摇色子()\n  console.log(result)\n}\n\nfn()\n",[31,524,522],{"__ignoreMap":29},[19,526,527,528,531],{},"用",[31,529,530],{"code":530},"try...catch","进行错误处理",[23,533,536],{"className":534,"code":535,"language":28,"meta":29},[26],"async function 摇色子() {\n  throw new Error('色子坏了')\n}\n\nasync function fn() {\n  try {\n    const result = await 摇色子()\n    console.log(result)\n  } catch (error) {\n    console.log(error)\n  }\n}\n\nfn()\n",[31,537,535],{"__ignoreMap":29},[15,539,541],{"id":540},"为什么需要-async","为什么需要 async",[19,543,544,545,547,548,551,552,554],{},"在函数前面加一个",[31,546,517],{"code":517},"，这看起来非常多余，",[31,549,550],{"code":550},"await","所在的函数就是",[31,553,517],{"code":517},"，不是吗？",[19,556,557],{},"理由之一：",[114,559,560],{},[19,561,562],{},"在 ES 标准的 async\u002Fawait 出来之前，有些人自己用函数实现了 await，为了兼容旧代码里普通函数的 await(xxx)（为了将旧代码里面的 await 和新的 ES 标准里的 async\u002Fawait 区分开来），其实 async 本身并没有什么意义。",[19,564,565,566,568,569,571,572,574],{},"你可能会说，",[31,567,517],{"code":517},"函数会隐式地返回一个 Promise 对象呀，但这并不能成为必须要在函数前加",[31,570,517],{"code":517},"的理由，有兴趣的可以去看看知乎上关于",[31,573,517],{"code":517},"的讨论。",[38,576,577,584],{},[41,578,579],{},[106,580,583],{"href":581,"rel":582},"https:\u002F\u002Fwww.zhihu.com\u002Fquestion\u002F308089255",[110],"为什么js里使用了await的方法必须定义成async的？",[41,585,586],{},[106,587,590],{"href":588,"rel":589},"https:\u002F\u002Fwww.zhihu.com\u002Fquestion\u002F298620447",[110],"C#中，async关键字到底起什么作用？",[10,592,594],{"id":593},"await-错误处理","await 错误处理",[19,596,597],{},"用 try\u002Fcatch 来同时处理同步和异步错误是很常见的做法",[23,599,602],{"className":600,"code":601,"language":28,"meta":29},[26],"let response\ntry {\n  response = await axios.get('\u002Fxxx')\n} catch (e) {\n  if (e.response) {\n    console.log(e.response.status)\n    throw e\n  }\n}\nconsole.log(response)\n",[31,603,601],{"__ignoreMap":29},[19,605,606],{},"但其实还有更好的写法，就像下面这样",[23,608,611],{"className":609,"code":610,"language":28,"meta":29},[26],"const errorHandler = error => {\n  console.log(error)\n  \u002F\u002F 注意这里要抛出一个错误\n  throw error\n  \u002F\u002F 或者 return Promise.reject(error)，注意：一定要 return\n}\n\n\u002F\u002F 只用一句代码就可以处理成功和失败\nconst response = await axios.get('\u002Fxxx').then(null, errorHandler)\n\n\u002F\u002F 或者使用 catch 语法糖\nconst response = await axios.get('\u002Fxxx').catch(errorHandler)\n",[31,612,610],{"__ignoreMap":29},[114,614,615],{},[19,616,617,618,621,622,625,626,629,630,632,633,635,636,638,639,641],{},"需要注意的是，",[31,619,620],{"code":620},"errorHandler","函数中不要直接",[31,623,624],{"code":624},"return","一个值，一定要",[45,627,628],{},"抛出一个错误","（打断程序的运行）。因为在请求调用失败的情况下，会把",[31,631,620],{"code":620},"里",[31,634,624],{"code":624},"的值直接赋值给 response（通俗的说法就是“Promise 会吃掉错误”），在",[31,637,620],{"code":620},"中抛出一个错误能够保证在请求成功的情况下才会有 response，请求失败的情况下一定是会进入",[31,640,620],{"code":620},"函数中的",[19,643,644],{},"下面是一个实际的例子",[23,646,649],{"className":647,"code":648,"language":28,"meta":29},[26],"const ajax = function() {\n  return new Promise((resolve, reject) => {\n    reject('这是失败后的提示')\n    \u002F\u002F resolve('这是成功后的结果')\n  })\n}\n\nconst error = (error) => {\n  console.log('error:', error)\n  return Promise.reject(error)\n}\n\nasync function fn() {\n  const response = await ajax().then(null, error)\n  console.log('response:', response)\n}\n\nfn()\n",[31,650,648],{"__ignoreMap":29},[19,652,653],{},"可以看到，我们仅仅只用了一句代码就可以同时处理 Promise 成功和失败的情况了，绝大多数的 ajax 调用都是可以用这样的方式来处理的。",[19,655,656,657,660,661,663],{},"所以，对于",[31,658,659],{"code":659},"async\u002Fawait","，并不是一定需要使用",[31,662,425],{"code":425},"来做错误处理的。",[19,665,666,667,669,670,672,673,675,676,678],{},"之前我常常陷入一个误区：就是认为",[31,668,550],{"code":550},"和",[31,671,235],{"code":235},"是对立的，始终觉得用了",[31,674,550],{"code":550},"后就不应该再出现",[31,677,235],{"code":235},"。",[19,680,681,682,684,685,687,688,669,690,692,693,695,696,698],{},"但其实并非如此，说到底",[31,683,659],{"code":659},"也只不过是",[31,686,235],{"code":235},"的语法糖而已。就像上面的例子一样，",[31,689,235],{"code":235},[31,691,550],{"code":550},"完全是可以结合在一起使用的，在",[31,694,235],{"code":235},"中进行错误处理，而",[31,697,550],{"code":550},"左边只接受成功结果。",[19,700,701,702,704],{},"另外，我们还可以把 4xx\u002F5xx 等常见错误用拦截器全局处理，",[31,703,620],{"code":620},"也可以放在拦截器里。",[15,706,708],{"id":707},"await-的传染性","await 的传染性",[19,710,711],{},"代码：",[23,713,716],{"className":714,"code":715,"language":28,"meta":29},[26],"console.log(1)\nawait console.log(2)\nconsole.log(3) \u002F\u002F await 会使这句代码变成异步的，如果想要让他立即执行，放到 await 前面即可\n",[31,717,715],{"__ignoreMap":29},[19,719,720],{},"分析：",[38,722,723,732,738,750],{},[41,724,725,727,728,731],{},[31,726,550],{"code":550},"会使得所有它",[45,729,730],{},"左边的和下面的代码","变成异步代码",[41,733,734,737],{},[31,735,736],{"code":736},"console.log(3)","变成异步任务了",[41,739,740,741,743,744,746,747,749],{},"Promise 同样有传染性（同步变异步），放到",[31,742,235],{"code":235},"回调函数中的代码会变成异步的，不过相比于",[31,745,550],{"code":550},"，",[31,748,235],{"code":235},"下面的代码并不会变成异步的",[41,751,752],{},"回调没有传染性",[15,754,756],{"id":755},"await-的应用场景","await 的应用场景",[73,758,318],{"id":759},"多次处理一个结果-1",[23,761,764],{"className":762,"code":763,"language":28,"meta":29},[26],"const r1 = await makePromise()\nconst r2 = handleR1(r1)\nconst r3 = handleR2(r2)\n",[31,765,763],{"__ignoreMap":29},[73,767,330],{"id":768},"串行-1",[19,770,771,772,774],{},"天生串行（多个",[31,773,550],{"code":550},"并排时，从上到下依次执行，后面的会等前面执行完了再执行）",[23,776,779],{"className":777,"code":778,"language":28,"meta":29},[26],"await promise1\nawait promise2\nawait promise3\n...\n",[31,780,778],{"__ignoreMap":29},[73,782,353],{"id":783},"并行-1",[19,785,786,787,359,790,359,793,796],{},"同 Promise，",[31,788,789],{"code":789},"await Promise.all([p1, p2, p3])",[31,791,792],{"code":792},"await Promise.allSettled([p1, p2, p3])",[31,794,795],{"code":795},"await Promise.race([p1, p2, p3])"," 都是并行的",[73,798,800],{"id":799},"循环的时候存在-bug","循环的时候存在 bug",[19,802,803,804,806],{},"正常情况下，即便在循环中，",[31,805,550],{"code":550},"也应当是串行执行的。",[19,808,809,810,813],{},"例如 ",[45,811,812],{},"for 循环中的 await 是串行的","（后面等前面）",[23,815,818],{"className":816,"code":817,"language":28,"meta":29},[26],"async function runPromiseByQueue(myPromises) {\n  for (let i = 0; i \u003C myPromises.length; i++) {\n    await myPromises[i]();\n  }\n}\n\nconst createPromise = (time, id) => () =>\n  new Promise((resolve) =>\n    setTimeout(() => {\n      console.log(\"promise\", id);\n      resolve();\n    }, time)\n  );\nrunPromiseByQueue([\n  createPromise(3000, 4),\n  createPromise(2000, 2),\n  createPromise(1000, 1)\n]);\n\n\u002F\u002F 4 2 1\n",[31,819,817],{"__ignoreMap":29},[19,821,822,823,826],{},"但是在某些循环中，如 ",[45,824,825],{},"forEach 和 map 中，await 会并行执行","（后面不等前面）",[23,828,831],{"className":829,"code":830,"language":28,"meta":29},[26],"async function runPromiseByQueue(myPromises) {\n  myPromises.forEach(async (task) => {\n    await task();\n  });\n}\n\nconst createPromise = (time, id) => () =>\n  new Promise((resolve) =>\n    setTimeout(() => {\n      console.log(\"promise\", id);\n      resolve();\n    }, time)\n  );\nrunPromiseByQueue([\n  createPromise(3000, 4),\n  createPromise(2000, 2),\n  createPromise(1000, 1)\n]);\n\n\u002F\u002F 1 2 4\n",[31,832,830],{"__ignoreMap":29},[19,834,835,836,841],{},"后面 JS 又出了一个新的东西 ",[106,837,840],{"href":838,"rel":839},"https:\u002F\u002Fes6.ruanyifeng.com\u002F#docs\u002Fasync-iterator#for-await---of",[110],"for await...of"," 来弥补这个 bug",[23,843,846],{"className":844,"code":845,"language":28,"meta":29},[26],"async function runPromiseByQueue(myPromises) {\n  \u002F\u002F 异步迭代\n  for await (let item of myPromises) {\n    console.log('promise', item);\n  }\n}\n\nconst createPromise = (time, id) =>\n  new Promise((resolve) =>\n    setTimeout(() => {\n      resolve(id);\n    }, time)\n  );\n\nrunPromiseByQueue([\n  createPromise(3000, 4),\n  createPromise(2000, 2),\n  createPromise(1000, 1)\n]);\n\n\u002F\u002F 4 2 1\n",[31,847,845],{"__ignoreMap":29},{"title":29,"searchDepth":849,"depth":849,"links":850},4,[851,879,880,885],{"id":12,"depth":852,"text":13,"children":853},2,[854,856,857,869,874],{"id":17,"depth":855,"text":17},3,{"id":35,"depth":855,"text":36},{"id":70,"depth":855,"text":71,"children":858},[859,861,863,865,867],{"id":75,"depth":849,"text":860},"Promise.resolve(result): 制造一个成功（或失败）",{"id":131,"depth":849,"text":862},"Promise.reject(reason): 制造一个失败",{"id":144,"depth":849,"text":864},"Promise.all(数组): 等待全部成功，或者有一个失败",{"id":255,"depth":849,"text":866},"Promise.allSettled(数组): 等待全部状态改变",{"id":290,"depth":849,"text":868},"Promise.race(数组): 等待第一个状态改变",{"id":314,"depth":855,"text":315,"children":870},[871,872,873],{"id":318,"depth":849,"text":318},{"id":330,"depth":849,"text":330},{"id":353,"depth":849,"text":353},{"id":376,"depth":855,"text":377,"children":875},[876,877,878],{"id":380,"depth":849,"text":380},{"id":428,"depth":849,"text":428},{"id":439,"depth":849,"text":439},{"id":450,"depth":852,"text":451},{"id":488,"depth":852,"text":489,"children":881},[882,883,884],{"id":492,"depth":855,"text":493},{"id":511,"depth":855,"text":512},{"id":540,"depth":855,"text":541},{"id":593,"depth":852,"text":594,"children":886},[887,888],{"id":707,"depth":855,"text":708},{"id":755,"depth":855,"text":756,"children":889},[890,891,892,893],{"id":759,"depth":849,"text":318},{"id":768,"depth":849,"text":330},{"id":783,"depth":849,"text":353},{"id":799,"depth":849,"text":800},[895],"学习笔记","2021-08-17",false,"md","\u002Fimages\u002Fpromise-async-await\u002Fcover.png",{"comments":897,"slots":901},{},true,"\u002F2021\u002Fpromise-async-await",null,{"text":906,"minutes":907,"time":908,"words":909},"15 min read",14.285,857100,2857,[911,914,917,920,923],{"title":912,"link":913},"异步操作概述","https:\u002F\u002Fwangdoc.com\u002Fjavascript\u002Fasync\u002Fgeneral.html",{"title":915,"link":916},"事件循环：微任务和宏任务","https:\u002F\u002Fzh.javascript.info\u002Fevent-loop",{"title":918,"link":919},"Event Loop、计时器、nextTick","https:\u002F\u002Fjuejin.cn\u002Fpost\u002F6844903582538399752",{"title":921,"link":922},"理解和使用Promise.all和Promise.race","https:\u002F\u002Fwww.jianshu.com\u002Fp\u002F7e60fc1be1b2",{"title":924,"link":925},"For await of","https:\u002F\u002Fwww.jianshu.com\u002Fp\u002F41fd1fccf472",{"title":5,"description":29},{"loc":903,"images":928},[929,930],{"loc":252},{"loc":273},"posts\u002F2021\u002Fpromise-async-await",[13,659],"tech","wzw8I8nvjf7EkCMSb0Pgbb464S26lYXy256P0IjFjpA",[936,941],{"title":937,"path":938,"stem":939,"date":940,"type":933,"children":-1},"JavaScript Snippets","\u002F2021\u002Fjavascript-snippets","posts\u002F2021\u002Fjavascript-snippets","2021-07-23",{"title":942,"path":943,"stem":944,"date":945,"type":933,"children":-1},"DNS 与 TCP","\u002F2021\u002Fdns-and-tcp","posts\u002F2021\u002Fdns-and-tcp","2021-08-20",1793699839762]