[{"data":1,"prerenderedAt":1930},["ShallowReactive",2],{"\u002Fposts\u002Fjs-why-not":3,"surround:\u002Fposts\u002Fjs-why-not":1921},{"id":4,"title":5,"body":6,"createdAt":1914,"description":41,"extension":1915,"meta":1916,"navigation":234,"path":1917,"seo":1918,"stem":1919,"__hash__":1920},"posts\u002Fposts\u002F2020\u002Fjs-why-not.md","JS Why Not 系列",{"type":7,"value":8,"toc":1900},"minimark",[9,13,18,27,32,35,84,91,122,125,164,170,174,177,328,341,351,413,416,468,472,478,593,606,701,707,829,833,848,861,864,880,887,895,898,915,922,926,933,936,1155,1158,1354,1357,1364,1368,1371,1483,1486,1546,1549,1552,1613,1616,1745,1748,1816,1819,1822,1873,1876,1880,1890,1893,1896],[10,11,5],"h1",{"id":12},"js-why-not-系列",[14,15,17],"h2",{"id":16},"optional-chaining","为什么避免使用 ?. 操作符？",[19,20,21,22,26],"p",{},"这里还是首先澄清一下，这一段",[23,24,25],"strong",{},"并不是说不要使用","可选链操作符，相反，可选链操作符由于语言支持的原因，比起任何其他方案（比如 lodash.get 或者 undefsafe）都要好（例如，对于类型系统的支持、JS 引擎优化等等）。那么，为什么还是要单独提出避免使用呢？",[28,29,31],"h3",{"id":30},"empty-object-pattern","空对象模式",[19,33,34],{},"实际上这里并不针对可选链单种操作符：",[36,37,42],"pre",{"className":38,"code":39,"language":40,"meta":41,"style":41},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F Optional chaining\nconst myVar = myObject.prop_a?.prop_b\n","js","",[43,44,45,54],"code",{"__ignoreMap":41},[46,47,50],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"sHwdD","\u002F\u002F Optional chaining\n",[46,55,57,61,65,69,72,75,78,81],{"class":48,"line":56},2,[46,58,60],{"class":59},"spNyl","const",[46,62,64],{"class":63},"sTEyZ"," myVar ",[46,66,68],{"class":67},"sMK4o","=",[46,70,71],{"class":63}," myObject",[46,73,74],{"class":67},".",[46,76,77],{"class":63},"prop_a",[46,79,80],{"class":67},"?.",[46,82,83],{"class":63},"prop_b\n",[19,85,86,87,90],{},"以下的写法同样是需要斟酌的（",[23,88,89],{},"或操作符","）：",[36,92,94],{"className":38,"code":93,"language":40,"meta":41,"style":41},"\u002F\u002F OR operator\nconst myVar = myObject.prop_a || {}\n",[43,95,96,101],{"__ignoreMap":41},[46,97,98],{"class":48,"line":49},[46,99,100],{"class":52},"\u002F\u002F OR operator\n",[46,102,103,105,107,109,111,113,116,119],{"class":48,"line":56},[46,104,60],{"class":59},[46,106,64],{"class":63},[46,108,68],{"class":67},[46,110,71],{"class":63},[46,112,74],{"class":67},[46,114,115],{"class":63},"prop_a ",[46,117,118],{"class":67},"||",[46,120,121],{"class":67}," {}\n",[19,123,124],{},"这些写法本质上是一样的。考虑第一个可选链的例子，实际上等价于",[36,126,128],{"className":38,"code":127,"language":40,"meta":41,"style":41},"const propA = myObject.prop_a || {}\nconst myVar = propA.prop_b\n",[43,129,130,149],{"__ignoreMap":41},[46,131,132,134,137,139,141,143,145,147],{"class":48,"line":49},[46,133,60],{"class":59},[46,135,136],{"class":63}," propA ",[46,138,68],{"class":67},[46,140,71],{"class":63},[46,142,74],{"class":67},[46,144,115],{"class":63},[46,146,118],{"class":67},[46,148,121],{"class":67},[46,150,151,153,155,157,160,162],{"class":48,"line":56},[46,152,60],{"class":59},[46,154,64],{"class":63},[46,156,68],{"class":67},[46,158,159],{"class":63}," propA",[46,161,74],{"class":67},[46,163,83],{"class":63},[19,165,166,167,169],{},"某些情况下喜欢用",[23,168,31],{},"来形容这种操作。所谓空对象模式就是，当某个数据为空时，将其作为一个空对象处理。",[28,171,173],{"id":172},"type-issue","类型问题",[19,175,176],{},"空对象模式有一定的好处。例如，考虑以下例子：",[36,178,180],{"className":38,"code":179,"language":40,"meta":41,"style":41},"function getFoo(arg) {\n  return arg.foo || {}\n}\n\nfunction getBar(arg) {\n  return getFoo(arg).bar || {}\n}\n\nfunction getBaz(arg) {\n  return getBar(arg).baz || {}\n}\n",[43,181,182,204,223,229,236,252,275,280,285,301,323],{"__ignoreMap":41},[46,183,184,187,191,194,198,201],{"class":48,"line":49},[46,185,186],{"class":59},"function",[46,188,190],{"class":189},"s2Zo4"," getFoo",[46,192,193],{"class":67},"(",[46,195,197],{"class":196},"sHdIc","arg",[46,199,200],{"class":67},")",[46,202,203],{"class":67}," {\n",[46,205,206,210,213,215,218,221],{"class":48,"line":56},[46,207,209],{"class":208},"s7zQu","  return",[46,211,212],{"class":63}," arg",[46,214,74],{"class":67},[46,216,217],{"class":63},"foo",[46,219,220],{"class":67}," ||",[46,222,121],{"class":67},[46,224,226],{"class":48,"line":225},3,[46,227,228],{"class":67},"}\n",[46,230,232],{"class":48,"line":231},4,[46,233,235],{"emptyLinePlaceholder":234},true,"\n",[46,237,239,241,244,246,248,250],{"class":48,"line":238},5,[46,240,186],{"class":59},[46,242,243],{"class":189}," getBar",[46,245,193],{"class":67},[46,247,197],{"class":196},[46,249,200],{"class":67},[46,251,203],{"class":67},[46,253,255,257,259,262,264,266,268,271,273],{"class":48,"line":254},6,[46,256,209],{"class":208},[46,258,190],{"class":189},[46,260,193],{"class":261},"swJcz",[46,263,197],{"class":63},[46,265,200],{"class":261},[46,267,74],{"class":67},[46,269,270],{"class":63},"bar",[46,272,220],{"class":67},[46,274,121],{"class":67},[46,276,278],{"class":48,"line":277},7,[46,279,228],{"class":67},[46,281,283],{"class":48,"line":282},8,[46,284,235],{"emptyLinePlaceholder":234},[46,286,288,290,293,295,297,299],{"class":48,"line":287},9,[46,289,186],{"class":59},[46,291,292],{"class":189}," getBaz",[46,294,193],{"class":67},[46,296,197],{"class":196},[46,298,200],{"class":67},[46,300,203],{"class":67},[46,302,304,306,308,310,312,314,316,319,321],{"class":48,"line":303},10,[46,305,209],{"class":208},[46,307,243],{"class":189},[46,309,193],{"class":261},[46,311,197],{"class":63},[46,313,200],{"class":261},[46,315,74],{"class":67},[46,317,318],{"class":63},"baz",[46,320,220],{"class":67},[46,322,121],{"class":67},[46,324,326],{"class":48,"line":325},11,[46,327,228],{"class":67},[19,329,330,331,333,334,336,337,340],{},"当传入的 ",[43,332,197],{}," 参数缺少 ",[43,335,217],{}," 时，调用 ",[43,338,339],{},"getBaz"," 依然可以保证不出错。",[19,342,343,344,347,348,350],{},"但是空对象模式仍然是一种相当",[23,345,346],{},"反模式","的用法。考虑上述的 ",[43,349,197],{}," 参数的类型，本应为",[36,352,354],{"className":38,"code":353,"language":40,"meta":41,"style":41},"type Arg = {\n  foo: {\n    bar: {\n      baz: unknown\n    }\n  }\n}\n",[43,355,356,370,380,389,399,404,409],{"__ignoreMap":41},[46,357,358,361,365,368],{"class":48,"line":49},[46,359,360],{"class":59},"type",[46,362,364],{"class":363},"sBMFI"," Arg",[46,366,367],{"class":67}," =",[46,369,203],{"class":67},[46,371,372,375,378],{"class":48,"line":56},[46,373,374],{"class":261},"  foo",[46,376,377],{"class":67},":",[46,379,203],{"class":67},[46,381,382,385,387],{"class":48,"line":225},[46,383,384],{"class":261},"    bar",[46,386,377],{"class":67},[46,388,203],{"class":67},[46,390,391,394,396],{"class":48,"line":231},[46,392,393],{"class":261},"      baz",[46,395,377],{"class":67},[46,397,398],{"class":363}," unknown\n",[46,400,401],{"class":48,"line":238},[46,402,403],{"class":67},"    }\n",[46,405,406],{"class":48,"line":254},[46,407,408],{"class":67},"  }\n",[46,410,411],{"class":48,"line":277},[46,412,228],{"class":67},[19,414,415],{},"但是当我们使用空对象时，实际上这个类型数据在后续的调用中丢失了，意味着它变成了",[36,417,419],{"className":38,"code":418,"language":40,"meta":41,"style":41},"type Arg = {\n  foo?: {\n    bar?: {\n      baz: unknown\n    }\n  }\n}\n",[43,420,421,431,440,448,456,460,464],{"__ignoreMap":41},[46,422,423,425,427,429],{"class":48,"line":49},[46,424,360],{"class":59},[46,426,364],{"class":363},[46,428,367],{"class":67},[46,430,203],{"class":67},[46,432,433,435,438],{"class":48,"line":56},[46,434,374],{"class":261},[46,436,437],{"class":67},"?:",[46,439,203],{"class":67},[46,441,442,444,446],{"class":48,"line":225},[46,443,384],{"class":261},[46,445,437],{"class":67},[46,447,203],{"class":67},[46,449,450,452,454],{"class":48,"line":231},[46,451,393],{"class":261},[46,453,377],{"class":67},[46,455,398],{"class":363},[46,457,458],{"class":48,"line":238},[46,459,403],{"class":67},[46,461,462],{"class":48,"line":254},[46,463,408],{"class":67},[46,465,466],{"class":48,"line":277},[46,467,228],{"class":67},[28,469,471],{"id":470},"downward-pollution","向下污染",[19,473,474,475,477],{},"上面的例子实际上说明了，使用可选链或者或操作符默认值的一个缺陷是",[23,476,471],{},"。考虑一个具体的例子，假设我们在写一个组件：",[36,479,481],{"className":38,"code":480,"language":40,"meta":41,"style":41},"const FooComponent = props => (\n  \u003Cdiv>{props.info.name}\u003C\u002Fdiv>\n)\n\nconst BarComponent = props => (\n  \u003CFooComponent info={props.data.user.info} \u002F>\n)\n",[43,482,483,501,533,538,542,557,589],{"__ignoreMap":41},[46,484,485,487,490,492,495,498],{"class":48,"line":49},[46,486,60],{"class":59},[46,488,489],{"class":63}," FooComponent ",[46,491,68],{"class":67},[46,493,494],{"class":196}," props",[46,496,497],{"class":59}," =>",[46,499,500],{"class":63}," (\n",[46,502,503,506,509,512,515,517,520,522,525,528,530],{"class":48,"line":56},[46,504,505],{"class":67},"  \u003C",[46,507,508],{"class":261},"div",[46,510,511],{"class":67},">{",[46,513,514],{"class":63},"props",[46,516,74],{"class":67},[46,518,519],{"class":63},"info",[46,521,74],{"class":67},[46,523,524],{"class":63},"name",[46,526,527],{"class":67},"}\u003C\u002F",[46,529,508],{"class":261},[46,531,532],{"class":67},">\n",[46,534,535],{"class":48,"line":225},[46,536,537],{"class":63},")\n",[46,539,540],{"class":48,"line":231},[46,541,235],{"emptyLinePlaceholder":234},[46,543,544,546,549,551,553,555],{"class":48,"line":238},[46,545,60],{"class":59},[46,547,548],{"class":63}," BarComponent ",[46,550,68],{"class":67},[46,552,494],{"class":196},[46,554,497],{"class":59},[46,556,500],{"class":63},[46,558,559,561,564,567,570,572,574,577,579,582,584,586],{"class":48,"line":254},[46,560,505],{"class":67},[46,562,563],{"class":363},"FooComponent",[46,565,566],{"class":59}," info",[46,568,569],{"class":67},"={",[46,571,514],{"class":63},[46,573,74],{"class":67},[46,575,576],{"class":63},"data",[46,578,74],{"class":67},[46,580,581],{"class":63},"user",[46,583,74],{"class":67},[46,585,519],{"class":63},[46,587,588],{"class":67},"} \u002F>\n",[46,590,591],{"class":48,"line":277},[46,592,537],{"class":63},[19,594,595,596,599,600,602,603,605],{},"可能正常情况下这个组件工作得很好，但某个情况下，传给 ",[43,597,598],{},"BarComponent"," 的 ",[43,601,576],{}," 没有 ",[43,604,581],{}," 这个属性。按照上面的模式，代码会变成",[36,607,609],{"className":38,"code":608,"language":40,"meta":41,"style":41},"const FooComponent = props => (\n  \u003Cdiv>{props.info?.name}\u003C\u002Fdiv>\n)\n\nconst BarComponent = props => (\n  \u003CFooComponent info={props.data.user?.info} \u002F>\n)\n",[43,610,611,625,649,653,657,671,697],{"__ignoreMap":41},[46,612,613,615,617,619,621,623],{"class":48,"line":49},[46,614,60],{"class":59},[46,616,489],{"class":63},[46,618,68],{"class":67},[46,620,494],{"class":196},[46,622,497],{"class":59},[46,624,500],{"class":63},[46,626,627,629,631,633,635,637,639,641,643,645,647],{"class":48,"line":56},[46,628,505],{"class":67},[46,630,508],{"class":261},[46,632,511],{"class":67},[46,634,514],{"class":63},[46,636,74],{"class":67},[46,638,519],{"class":63},[46,640,80],{"class":67},[46,642,524],{"class":63},[46,644,527],{"class":67},[46,646,508],{"class":261},[46,648,532],{"class":67},[46,650,651],{"class":48,"line":225},[46,652,537],{"class":63},[46,654,655],{"class":48,"line":231},[46,656,235],{"emptyLinePlaceholder":234},[46,658,659,661,663,665,667,669],{"class":48,"line":238},[46,660,60],{"class":59},[46,662,548],{"class":63},[46,664,68],{"class":67},[46,666,494],{"class":196},[46,668,497],{"class":59},[46,670,500],{"class":63},[46,672,673,675,677,679,681,683,685,687,689,691,693,695],{"class":48,"line":254},[46,674,505],{"class":67},[46,676,563],{"class":363},[46,678,566],{"class":59},[46,680,569],{"class":67},[46,682,514],{"class":63},[46,684,74],{"class":67},[46,686,576],{"class":63},[46,688,74],{"class":67},[46,690,581],{"class":63},[46,692,80],{"class":67},[46,694,519],{"class":63},[46,696,588],{"class":67},[46,698,699],{"class":48,"line":277},[46,700,537],{"class":63},[19,702,703,704,706],{},"原本只是 BarComponent 对数据需要额外处理，现在变成了 ",[43,705,563],{}," 也需要修改对应的实现了！这里显然，一个更好的解决方案是",[36,708,710],{"className":38,"code":709,"language":40,"meta":41,"style":41},"const FooComponent = props => (\n  \u003Cdiv>{props.info.name}\u003C\u002Fdiv>\n)\n\nconst BarComponent = props => (\n  props.data.user ? \u003CFooComponent info={props.data.user.info} \u002F> : \u003Cdiv \u002F>\n)\n",[43,711,712,726,750,754,758,772,825],{"__ignoreMap":41},[46,713,714,716,718,720,722,724],{"class":48,"line":49},[46,715,60],{"class":59},[46,717,489],{"class":63},[46,719,68],{"class":67},[46,721,494],{"class":196},[46,723,497],{"class":59},[46,725,500],{"class":63},[46,727,728,730,732,734,736,738,740,742,744,746,748],{"class":48,"line":56},[46,729,505],{"class":67},[46,731,508],{"class":261},[46,733,511],{"class":67},[46,735,514],{"class":63},[46,737,74],{"class":67},[46,739,519],{"class":63},[46,741,74],{"class":67},[46,743,524],{"class":63},[46,745,527],{"class":67},[46,747,508],{"class":261},[46,749,532],{"class":67},[46,751,752],{"class":48,"line":225},[46,753,537],{"class":63},[46,755,756],{"class":48,"line":231},[46,757,235],{"emptyLinePlaceholder":234},[46,759,760,762,764,766,768,770],{"class":48,"line":238},[46,761,60],{"class":59},[46,763,548],{"class":63},[46,765,68],{"class":67},[46,767,494],{"class":196},[46,769,497],{"class":59},[46,771,500],{"class":63},[46,773,774,777,779,781,783,786,789,792,794,796,798,800,802,804,806,808,810,812,815,818,820,822],{"class":48,"line":254},[46,775,776],{"class":63},"  props",[46,778,74],{"class":67},[46,780,576],{"class":63},[46,782,74],{"class":67},[46,784,785],{"class":63},"user ",[46,787,788],{"class":67},"?",[46,790,791],{"class":67}," \u003C",[46,793,563],{"class":363},[46,795,566],{"class":59},[46,797,569],{"class":67},[46,799,514],{"class":63},[46,801,74],{"class":67},[46,803,576],{"class":63},[46,805,74],{"class":67},[46,807,581],{"class":63},[46,809,74],{"class":67},[46,811,519],{"class":63},[46,813,814],{"class":67},"} \u002F>",[46,816,817],{"class":67}," :",[46,819,791],{"class":67},[46,821,508],{"class":261},[46,823,824],{"class":67}," \u002F>\n",[46,826,827],{"class":48,"line":277},[46,828,537],{"class":63},[28,830,832],{"id":831},"exposing","展现",[19,834,835,836,839,840,843,844,847],{},"另一个问题是，使用可选链\u002F或操作符可能错误地掩盖了我们遇到的问题。依然考虑上面的例子，当我们使用可选链来解决这个问题的时候，实际上我们解决的不是",[23,837,838],{},"问题","，而是",[23,841,842],{},"报错","。对于很多前端框架而言，代码依然在按照报错的情况运行，只是我们不再看得到 ",[43,845,846],{},"Cannot read property 'info' of undefined"," 了。通常这意味着我们忽略了真正的问题：",[849,850,851],"blockquote",{},[19,852,853,854,599,856,602,858,860],{},"为什么传给 ",[43,855,598],{},[43,857,576],{},[43,859,581],{}," 这个属性？",[19,862,863],{},"显然对于这个问题，在不同的场景下都应该有更合适的处理方式：",[865,866,867,871,874,877],"ul",{},[868,869,870],"li",{},"如果是加载状态引起的，是否应该在组件内展示一个加载中的图标，而不是直接什么都不展示？",[868,872,873],{},"如果是接口失败引起的，是否应该是给出一个加载失败的提示？",[868,875,876],{},"如果是数据格式变动引起的，是否应该考虑这里需要兼容新\u002F旧数据格式？",[868,878,879],{},"…或者，我们根本就用错了组件呢？",[19,881,882,883,886],{},"其实可选链操作符很多时候还是有用的，但这种情况通常只发生在【对应的参数原本就接受一个可以为空的数据】的情况。如果大家觉得不好判断的话，只要在用的时候多想想，是否",[23,884,885],{},"现在或者未来","会有其他边缘情况的问题就好了；或者思考一下，当我们使用 TypeScript 去写的时候，是否就会有问题暴露出来？",[14,888,890,891,894],{"id":889},"promise-methods","为什么避免使用 ",[43,892,893],{},".then()"," 等方法？",[19,896,897],{},"回顾一下异步处理的历史：",[865,899,900,903,906,909,912],{},[868,901,902],{},"ES3：callback",[868,904,905],{},"ES2015：Promise、co",[868,907,908],{},"ES2017：async function",[868,910,911],{},"ES2019：async iterator",[868,913,914],{},"……",[19,916,917,918,921],{},"异步处理的方式随着语言的发展越来越多，我们很多时候是全部都在使用的。但这里想要提的是，我们应当尽量避免使用 Promise 的",[23,919,920],{},"原型方法","，而是使用 async function 来代替。",[28,923,925],{"id":924},"readability","可读性",[19,927,928,929,932],{},"众所周知，async function 的可读性远大于 Promise。这是因为 Promise 本身就是异步操作的",[23,930,931],{},"原语","，相当于是说，Promise 实际上只提供了底层接口，而 async function 则是开发层面的使用方式。",[19,934,935],{},"简单对比一下：",[36,937,939],{"className":38,"code":938,"language":40,"meta":41,"style":41},"async function checkStatus(time, interval) {\n  return new Promise(resolve => {\n    function poll() {\n      if (time \u003C= 0) {\n        resolve(undefined)\n        return\n      }\n      fetchValue().then(value => {\n        if (value) {\n          resolve(value)\n          return\n        }\n        time -= 1\n        setTimeout(poll, interval)\n      })\n    }\n  })\n}\n",[43,940,941,967,986,999,1022,1034,1039,1044,1065,1078,1089,1094,1100,1112,1129,1137,1142,1150],{"__ignoreMap":41},[46,942,943,946,949,952,954,957,960,963,965],{"class":48,"line":49},[46,944,945],{"class":59},"async",[46,947,948],{"class":59}," function",[46,950,951],{"class":189}," checkStatus",[46,953,193],{"class":67},[46,955,956],{"class":196},"time",[46,958,959],{"class":67},",",[46,961,962],{"class":196}," interval",[46,964,200],{"class":67},[46,966,203],{"class":67},[46,968,969,971,974,977,979,982,984],{"class":48,"line":56},[46,970,209],{"class":208},[46,972,973],{"class":67}," new",[46,975,976],{"class":363}," Promise",[46,978,193],{"class":261},[46,980,981],{"class":196},"resolve",[46,983,497],{"class":59},[46,985,203],{"class":67},[46,987,988,991,994,997],{"class":48,"line":225},[46,989,990],{"class":59},"    function",[46,992,993],{"class":189}," poll",[46,995,996],{"class":67},"()",[46,998,203],{"class":67},[46,1000,1001,1004,1007,1009,1012,1016,1019],{"class":48,"line":231},[46,1002,1003],{"class":208},"      if",[46,1005,1006],{"class":261}," (",[46,1008,956],{"class":63},[46,1010,1011],{"class":67}," \u003C=",[46,1013,1015],{"class":1014},"sbssI"," 0",[46,1017,1018],{"class":261},") ",[46,1020,1021],{"class":67},"{\n",[46,1023,1024,1027,1029,1032],{"class":48,"line":238},[46,1025,1026],{"class":189},"        resolve",[46,1028,193],{"class":261},[46,1030,1031],{"class":67},"undefined",[46,1033,537],{"class":261},[46,1035,1036],{"class":48,"line":254},[46,1037,1038],{"class":208},"        return\n",[46,1040,1041],{"class":48,"line":277},[46,1042,1043],{"class":67},"      }\n",[46,1045,1046,1049,1051,1053,1056,1058,1061,1063],{"class":48,"line":282},[46,1047,1048],{"class":189},"      fetchValue",[46,1050,996],{"class":261},[46,1052,74],{"class":67},[46,1054,1055],{"class":189},"then",[46,1057,193],{"class":261},[46,1059,1060],{"class":196},"value",[46,1062,497],{"class":59},[46,1064,203],{"class":67},[46,1066,1067,1070,1072,1074,1076],{"class":48,"line":287},[46,1068,1069],{"class":208},"        if",[46,1071,1006],{"class":261},[46,1073,1060],{"class":63},[46,1075,1018],{"class":261},[46,1077,1021],{"class":67},[46,1079,1080,1083,1085,1087],{"class":48,"line":303},[46,1081,1082],{"class":189},"          resolve",[46,1084,193],{"class":261},[46,1086,1060],{"class":63},[46,1088,537],{"class":261},[46,1090,1091],{"class":48,"line":325},[46,1092,1093],{"class":208},"          return\n",[46,1095,1097],{"class":48,"line":1096},12,[46,1098,1099],{"class":67},"        }\n",[46,1101,1103,1106,1109],{"class":48,"line":1102},13,[46,1104,1105],{"class":63},"        time",[46,1107,1108],{"class":67}," -=",[46,1110,1111],{"class":1014}," 1\n",[46,1113,1115,1118,1120,1123,1125,1127],{"class":48,"line":1114},14,[46,1116,1117],{"class":189},"        setTimeout",[46,1119,193],{"class":261},[46,1121,1122],{"class":63},"poll",[46,1124,959],{"class":67},[46,1126,962],{"class":63},[46,1128,537],{"class":261},[46,1130,1132,1135],{"class":48,"line":1131},15,[46,1133,1134],{"class":67},"      }",[46,1136,537],{"class":261},[46,1138,1140],{"class":48,"line":1139},16,[46,1141,403],{"class":67},[46,1143,1145,1148],{"class":48,"line":1144},17,[46,1146,1147],{"class":67},"  }",[46,1149,537],{"class":261},[46,1151,1153],{"class":48,"line":1152},18,[46,1154,228],{"class":67},[19,1156,1157],{},"和",[36,1159,1161],{"className":38,"code":1160,"language":40,"meta":41,"style":41},"function wait(interval) {\n  return new Promise(resolve => setTimeout(resolve, interval))\n}\n\nasync function checkStatus(time, interval) {\n  let value\n  while (time > 0) {\n    try {\n      value = await fetchValue()\n      if (value) return value\n    } catch {\n      \u002F\u002F continue\n    }\n    await wait(interval)\n    time -= 1\n  }\n  return value\n}\n",[43,1162,1163,1179,1207,1211,1215,1235,1243,1261,1268,1284,1299,1309,1314,1318,1331,1340,1344,1350],{"__ignoreMap":41},[46,1164,1165,1167,1170,1172,1175,1177],{"class":48,"line":49},[46,1166,186],{"class":59},[46,1168,1169],{"class":189}," wait",[46,1171,193],{"class":67},[46,1173,1174],{"class":196},"interval",[46,1176,200],{"class":67},[46,1178,203],{"class":67},[46,1180,1181,1183,1185,1187,1189,1191,1193,1196,1198,1200,1202,1204],{"class":48,"line":56},[46,1182,209],{"class":208},[46,1184,973],{"class":67},[46,1186,976],{"class":363},[46,1188,193],{"class":261},[46,1190,981],{"class":196},[46,1192,497],{"class":59},[46,1194,1195],{"class":189}," setTimeout",[46,1197,193],{"class":261},[46,1199,981],{"class":63},[46,1201,959],{"class":67},[46,1203,962],{"class":63},[46,1205,1206],{"class":261},"))\n",[46,1208,1209],{"class":48,"line":225},[46,1210,228],{"class":67},[46,1212,1213],{"class":48,"line":231},[46,1214,235],{"emptyLinePlaceholder":234},[46,1216,1217,1219,1221,1223,1225,1227,1229,1231,1233],{"class":48,"line":238},[46,1218,945],{"class":59},[46,1220,948],{"class":59},[46,1222,951],{"class":189},[46,1224,193],{"class":67},[46,1226,956],{"class":196},[46,1228,959],{"class":67},[46,1230,962],{"class":196},[46,1232,200],{"class":67},[46,1234,203],{"class":67},[46,1236,1237,1240],{"class":48,"line":254},[46,1238,1239],{"class":59},"  let",[46,1241,1242],{"class":63}," value\n",[46,1244,1245,1248,1250,1252,1255,1257,1259],{"class":48,"line":277},[46,1246,1247],{"class":208},"  while",[46,1249,1006],{"class":261},[46,1251,956],{"class":63},[46,1253,1254],{"class":67}," >",[46,1256,1015],{"class":1014},[46,1258,1018],{"class":261},[46,1260,1021],{"class":67},[46,1262,1263,1266],{"class":48,"line":282},[46,1264,1265],{"class":208},"    try",[46,1267,203],{"class":67},[46,1269,1270,1273,1275,1278,1281],{"class":48,"line":287},[46,1271,1272],{"class":63},"      value",[46,1274,367],{"class":67},[46,1276,1277],{"class":208}," await",[46,1279,1280],{"class":189}," fetchValue",[46,1282,1283],{"class":261},"()\n",[46,1285,1286,1288,1290,1292,1294,1297],{"class":48,"line":303},[46,1287,1003],{"class":208},[46,1289,1006],{"class":261},[46,1291,1060],{"class":63},[46,1293,1018],{"class":261},[46,1295,1296],{"class":208},"return",[46,1298,1242],{"class":63},[46,1300,1301,1304,1307],{"class":48,"line":325},[46,1302,1303],{"class":67},"    }",[46,1305,1306],{"class":208}," catch",[46,1308,203],{"class":67},[46,1310,1311],{"class":48,"line":1096},[46,1312,1313],{"class":52},"      \u002F\u002F continue\n",[46,1315,1316],{"class":48,"line":1102},[46,1317,403],{"class":67},[46,1319,1320,1323,1325,1327,1329],{"class":48,"line":1114},[46,1321,1322],{"class":208},"    await",[46,1324,1169],{"class":189},[46,1326,193],{"class":261},[46,1328,1174],{"class":63},[46,1330,537],{"class":261},[46,1332,1333,1336,1338],{"class":48,"line":1131},[46,1334,1335],{"class":63},"    time",[46,1337,1108],{"class":67},[46,1339,1111],{"class":1014},[46,1341,1342],{"class":48,"line":1139},[46,1343,408],{"class":67},[46,1345,1346,1348],{"class":48,"line":1144},[46,1347,209],{"class":208},[46,1349,1242],{"class":63},[46,1351,1352],{"class":48,"line":1152},[46,1353,228],{"class":67},[19,1355,1356],{},"显然下面的可读性更好（而且是在上面的代码已经被抽象过的条件下，不然会更加糟糕）",[19,1358,1359,1360,1363],{},"为什么会有可读性的差异呢？除了所谓的可以写成同步的方式之外，一个比较重要的原因在于：我们可以在 async function 里使用",[23,1361,1362],{},"大部分的语言结构","（for\u002Fwhile\u002Ftry-catch 等），JS 已经为其做了同步\u002F异步的处理。",[28,1365,1367],{"id":1366},"asynchronous","异步化",[19,1369,1370],{},"继续上面的这一点来展开的话，之所以在 async function 内可以使用多数语言结构，一个重要的原因在于，await 操作本身语义也被统一了。考虑一下：",[36,1372,1374],{"className":38,"code":1373,"language":40,"meta":41,"style":41},"let value\nfunction getValue() {\n  if (value) return value\n  return new Promise(resolve => {\n    return fetchValue()\n  }).then((result) => {\n    value = result\n    return value\n  })\n}\n",[43,1375,1376,1383,1394,1409,1425,1434,1457,1467,1473,1479],{"__ignoreMap":41},[46,1377,1378,1381],{"class":48,"line":49},[46,1379,1380],{"class":59},"let",[46,1382,1242],{"class":63},[46,1384,1385,1387,1390,1392],{"class":48,"line":56},[46,1386,186],{"class":59},[46,1388,1389],{"class":189}," getValue",[46,1391,996],{"class":67},[46,1393,203],{"class":67},[46,1395,1396,1399,1401,1403,1405,1407],{"class":48,"line":225},[46,1397,1398],{"class":208},"  if",[46,1400,1006],{"class":261},[46,1402,1060],{"class":63},[46,1404,1018],{"class":261},[46,1406,1296],{"class":208},[46,1408,1242],{"class":63},[46,1410,1411,1413,1415,1417,1419,1421,1423],{"class":48,"line":231},[46,1412,209],{"class":208},[46,1414,973],{"class":67},[46,1416,976],{"class":363},[46,1418,193],{"class":261},[46,1420,981],{"class":196},[46,1422,497],{"class":59},[46,1424,203],{"class":67},[46,1426,1427,1430,1432],{"class":48,"line":238},[46,1428,1429],{"class":208},"    return",[46,1431,1280],{"class":189},[46,1433,1283],{"class":261},[46,1435,1436,1438,1440,1442,1444,1446,1448,1451,1453,1455],{"class":48,"line":254},[46,1437,1147],{"class":67},[46,1439,200],{"class":261},[46,1441,74],{"class":67},[46,1443,1055],{"class":189},[46,1445,193],{"class":261},[46,1447,193],{"class":67},[46,1449,1450],{"class":196},"result",[46,1452,200],{"class":67},[46,1454,497],{"class":59},[46,1456,203],{"class":67},[46,1458,1459,1462,1464],{"class":48,"line":277},[46,1460,1461],{"class":63},"    value",[46,1463,367],{"class":67},[46,1465,1466],{"class":63}," result\n",[46,1468,1469,1471],{"class":48,"line":282},[46,1470,1429],{"class":208},[46,1472,1242],{"class":63},[46,1474,1475,1477],{"class":48,"line":287},[46,1476,1147],{"class":67},[46,1478,537],{"class":261},[46,1480,1481],{"class":48,"line":303},[46,1482,228],{"class":67},[19,1484,1485],{},"上面的例子似乎也挺自然的，如果有一个缓存的值则使用这个值，如果没有则返回一个 Promise。就好像我们写",[36,1487,1489],{"className":38,"code":1488,"language":40,"meta":41,"style":41},"let value\nasync function getValue() {\n  if (value) return value\n  value = await fetchValue()\n  return value\n}\n",[43,1490,1491,1497,1509,1523,1536,1542],{"__ignoreMap":41},[46,1492,1493,1495],{"class":48,"line":49},[46,1494,1380],{"class":59},[46,1496,1242],{"class":63},[46,1498,1499,1501,1503,1505,1507],{"class":48,"line":56},[46,1500,945],{"class":59},[46,1502,948],{"class":59},[46,1504,1389],{"class":189},[46,1506,996],{"class":67},[46,1508,203],{"class":67},[46,1510,1511,1513,1515,1517,1519,1521],{"class":48,"line":225},[46,1512,1398],{"class":208},[46,1514,1006],{"class":261},[46,1516,1060],{"class":63},[46,1518,1018],{"class":261},[46,1520,1296],{"class":208},[46,1522,1242],{"class":63},[46,1524,1525,1528,1530,1532,1534],{"class":48,"line":231},[46,1526,1527],{"class":63},"  value",[46,1529,367],{"class":67},[46,1531,1277],{"class":208},[46,1533,1280],{"class":189},[46,1535,1283],{"class":261},[46,1537,1538,1540],{"class":48,"line":238},[46,1539,209],{"class":208},[46,1541,1242],{"class":63},[46,1543,1544],{"class":48,"line":254},[46,1545,228],{"class":67},[19,1547,1548],{},"好像也没什么区别呀？",[19,1550,1551],{},"但是，正如这篇文章要讨论的，考虑下面的调用情况",[36,1553,1555],{"className":38,"code":1554,"language":40,"meta":41,"style":41},"function printValue() {\n  getValue().then(value => {\n    console.log(value)\n  })\n}\n",[43,1556,1557,1568,1587,1603,1609],{"__ignoreMap":41},[46,1558,1559,1561,1564,1566],{"class":48,"line":49},[46,1560,186],{"class":59},[46,1562,1563],{"class":189}," printValue",[46,1565,996],{"class":67},[46,1567,203],{"class":67},[46,1569,1570,1573,1575,1577,1579,1581,1583,1585],{"class":48,"line":56},[46,1571,1572],{"class":189},"  getValue",[46,1574,996],{"class":261},[46,1576,74],{"class":67},[46,1578,1055],{"class":189},[46,1580,193],{"class":261},[46,1582,1060],{"class":196},[46,1584,497],{"class":59},[46,1586,203],{"class":67},[46,1588,1589,1592,1594,1597,1599,1601],{"class":48,"line":225},[46,1590,1591],{"class":63},"    console",[46,1593,74],{"class":67},[46,1595,1596],{"class":189},"log",[46,1598,193],{"class":261},[46,1600,1060],{"class":63},[46,1602,537],{"class":261},[46,1604,1605,1607],{"class":48,"line":231},[46,1606,1147],{"class":67},[46,1608,537],{"class":261},[46,1610,1611],{"class":48,"line":238},[46,1612,228],{"class":67},[19,1614,1615],{},"在第一个例子中，这段代码会有一个问题，就是当 value 有缓存值的时候，getValue 的返回值并没有 then 方法。除非：",[36,1617,1619],{"className":38,"code":1618,"language":40,"meta":41,"style":41},"function printValue() {\n  const result = getValue()\n  if (typeof result.then === 'function') {\n    result.then(value => {\n      console.log(value)\n    })\n  } else {\n    console.log(result)\n  }\n}\n",[43,1620,1621,1631,1645,1676,1693,1708,1714,1723,1737,1741],{"__ignoreMap":41},[46,1622,1623,1625,1627,1629],{"class":48,"line":49},[46,1624,186],{"class":59},[46,1626,1563],{"class":189},[46,1628,996],{"class":67},[46,1630,203],{"class":67},[46,1632,1633,1636,1639,1641,1643],{"class":48,"line":56},[46,1634,1635],{"class":59},"  const",[46,1637,1638],{"class":63}," result",[46,1640,367],{"class":67},[46,1642,1389],{"class":189},[46,1644,1283],{"class":261},[46,1646,1647,1649,1651,1654,1656,1658,1660,1663,1666,1669,1672,1674],{"class":48,"line":225},[46,1648,1398],{"class":208},[46,1650,1006],{"class":261},[46,1652,1653],{"class":67},"typeof",[46,1655,1638],{"class":63},[46,1657,74],{"class":67},[46,1659,1055],{"class":63},[46,1661,1662],{"class":67}," ===",[46,1664,1665],{"class":67}," '",[46,1667,186],{"class":1668},"sfazB",[46,1670,1671],{"class":67},"'",[46,1673,1018],{"class":261},[46,1675,1021],{"class":67},[46,1677,1678,1681,1683,1685,1687,1689,1691],{"class":48,"line":231},[46,1679,1680],{"class":63},"    result",[46,1682,74],{"class":67},[46,1684,1055],{"class":189},[46,1686,193],{"class":261},[46,1688,1060],{"class":196},[46,1690,497],{"class":59},[46,1692,203],{"class":67},[46,1694,1695,1698,1700,1702,1704,1706],{"class":48,"line":238},[46,1696,1697],{"class":63},"      console",[46,1699,74],{"class":67},[46,1701,1596],{"class":189},[46,1703,193],{"class":261},[46,1705,1060],{"class":63},[46,1707,537],{"class":261},[46,1709,1710,1712],{"class":48,"line":254},[46,1711,1303],{"class":67},[46,1713,537],{"class":261},[46,1715,1716,1718,1721],{"class":48,"line":277},[46,1717,1147],{"class":67},[46,1719,1720],{"class":208}," else",[46,1722,203],{"class":67},[46,1724,1725,1727,1729,1731,1733,1735],{"class":48,"line":282},[46,1726,1591],{"class":63},[46,1728,74],{"class":67},[46,1730,1596],{"class":189},[46,1732,193],{"class":261},[46,1734,1450],{"class":63},[46,1736,537],{"class":261},[46,1738,1739],{"class":48,"line":287},[46,1740,408],{"class":67},[46,1742,1743],{"class":48,"line":303},[46,1744,228],{"class":67},[19,1746,1747],{},"或者聪明的同学会想到",[36,1749,1751],{"className":38,"code":1750,"language":40,"meta":41,"style":41},"function printValue() {\n  Promise.resolve(getValue()).then(value => {\n    console.log(value)\n  })\n}\n",[43,1752,1753,1763,1792,1806,1812],{"__ignoreMap":41},[46,1754,1755,1757,1759,1761],{"class":48,"line":49},[46,1756,186],{"class":59},[46,1758,1563],{"class":189},[46,1760,996],{"class":67},[46,1762,203],{"class":67},[46,1764,1765,1768,1770,1772,1774,1777,1780,1782,1784,1786,1788,1790],{"class":48,"line":56},[46,1766,1767],{"class":363},"  Promise",[46,1769,74],{"class":67},[46,1771,981],{"class":189},[46,1773,193],{"class":261},[46,1775,1776],{"class":189},"getValue",[46,1778,1779],{"class":261},"())",[46,1781,74],{"class":67},[46,1783,1055],{"class":189},[46,1785,193],{"class":261},[46,1787,1060],{"class":196},[46,1789,497],{"class":59},[46,1791,203],{"class":67},[46,1793,1794,1796,1798,1800,1802,1804],{"class":48,"line":225},[46,1795,1591],{"class":63},[46,1797,74],{"class":67},[46,1799,1596],{"class":189},[46,1801,193],{"class":261},[46,1803,1060],{"class":63},[46,1805,537],{"class":261},[46,1807,1808,1810],{"class":48,"line":231},[46,1809,1147],{"class":67},[46,1811,537],{"class":261},[46,1813,1814],{"class":48,"line":238},[46,1815,228],{"class":67},[19,1817,1818],{},"然而，如果我们使用第二个例子是，实际上 async 关键字已经将我们的返回值统一成了 Promise，在调用的时候便没有后顾之忧了。",[19,1820,1821],{},"不仅如此，假设我们的调用是",[36,1823,1825],{"className":38,"code":1824,"language":40,"meta":41,"style":41},"async function printValue() {\n  const value = await getValue()\n  console.log(value)\n}\n",[43,1826,1827,1839,1854,1869],{"__ignoreMap":41},[46,1828,1829,1831,1833,1835,1837],{"class":48,"line":49},[46,1830,945],{"class":59},[46,1832,948],{"class":59},[46,1834,1563],{"class":189},[46,1836,996],{"class":67},[46,1838,203],{"class":67},[46,1840,1841,1843,1846,1848,1850,1852],{"class":48,"line":56},[46,1842,1635],{"class":59},[46,1844,1845],{"class":63}," value",[46,1847,367],{"class":67},[46,1849,1277],{"class":208},[46,1851,1389],{"class":189},[46,1853,1283],{"class":261},[46,1855,1856,1859,1861,1863,1865,1867],{"class":48,"line":225},[46,1857,1858],{"class":63},"  console",[46,1860,74],{"class":67},[46,1862,1596],{"class":189},[46,1864,193],{"class":261},[46,1866,1060],{"class":63},[46,1868,537],{"class":261},[46,1870,1871],{"class":48,"line":231},[46,1872,228],{"class":67},[19,1874,1875],{},"那么不论上面哪一种写法，都是可以工作的，因为 await 关键字也已经将返回值统一成了 Promise 来处理。这意味着当我们使用 async 或者 await 的时候，同步值也会作为异步来处理，也就不需要我们关心两者的区别。",[28,1877,1879],{"id":1878},"compatibility","兼容性",[19,1881,1882,1883,1885,1886,1889],{},"另一个问题是，",[23,1884,920],{},"只能 polyfill，而 async\u002Fawait 及其涉及的语法可以",[23,1887,1888],{},"转译","。两者的最大区别在于转译可以保证生产环境使用兼容的代码，而原型 polyfill 只能靠 browserslist 的声明来推断。",[19,1891,1892],{},"在这个问题上，最明显的表现是 Promise#finally（ES2019）。当某些浏览器没有被 browserslist 覆盖到时，使用 .finally 就会报错。",[19,1894,1895],{},"尽管目前在肉眼可见的未来内，Promise 对象不会有新的原型方法了，我们只需要对 .finally做特殊处理就可以；但是未来的事情，谁又能说清呢？:)",[1897,1898,1899],"style",{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":41,"searchDepth":56,"depth":56,"links":1901},[1902,1908],{"id":16,"depth":56,"text":17,"children":1903},[1904,1905,1906,1907],{"id":30,"depth":225,"text":31},{"id":172,"depth":225,"text":173},{"id":470,"depth":225,"text":471},{"id":831,"depth":225,"text":832},{"id":889,"depth":56,"text":1909,"children":1910},"为什么避免使用 .then() 等方法？",[1911,1912,1913],{"id":924,"depth":225,"text":925},{"id":1366,"depth":225,"text":1367},{"id":1878,"depth":225,"text":1879},"2020-06-22","md",{},"\u002Fposts\u002Fjs-why-not",{"title":5,"description":41},"posts\u002F2020\u002Fjs-why-not","y3gBXfkQ16zTd3oTpQoaxkJUmbqykOYOOQc8eaZjEks",[1922,1926],{"title":1923,"path":1924,"stem":1925,"children":-1},"让代码更具可读性","\u002Fposts\u002Fmake-your-code-more-readable","posts\u002F2020\u002Fmake-your-code-more-readable",{"title":1927,"path":1928,"stem":1929,"children":-1},"TypeScript 开发技巧","\u002Fposts\u002Ftypescript-development-tips","posts\u002F2020\u002Ftypescript-development-tips",1792500630765]