[{"data":1,"prerenderedAt":4645},["ShallowReactive",2],{"content:\u002F03-concurrency\u002F03-context":3},{"title":4,"description":5,"path":6,"body":7},"Context в Go","Представьте: пользователь отправил HTTP-запрос, который запускает несколько горутин — одна ходит в базу, другая в Redis, третья вызывает внешний API. Пользователь не дождался и закрыл вкладку. Без механизма отмены все три горутины продолжат работу — тратят ресурсы, держат соединения, делают бессмысленную работу. context.Context — это именно тот механизм, который решает эту проблему.","\u002F03-concurrency\u002F03-context",{"type":8,"value":9,"toc":4620},"minimark",[10,14,23,26,31,37,162,169,267,270,272,276,279,380,383,385,389,392,447,456,458,462,468,676,685,690,823,838,1005,1007,1011,1014,1155,1164,1168,1171,1382,1386,1556,1560,1563,1686,1688,1692,1702,1971,1975,1978,2088,2091,2095,2103,2157,2160,2162,2166,2169,2410,2412,2416,2420,2577,2580,2584,2640,2644,2697,2699,2703,2712,2740,2751,2759,2771,2779,2787,2812,2820,2824,2826,2831,2837,2843,2860,2865,3206,3208,3213,3218,3223,3232,3236,3766,3768,3773,3778,3783,3788,4110,4115,4123,4127,4194,4196,4200,4249,4412,4616],[11,12,4],"h1",{"id":13},"context-в-go",[15,16,17,18,22],"p",{},"Представьте: пользователь отправил HTTP-запрос, который запускает несколько горутин — одна ходит в базу, другая в Redis, третья вызывает внешний API. Пользователь не дождался и закрыл вкладку. Без механизма отмены все три горутины продолжат работу — тратят ресурсы, держат соединения, делают бессмысленную работу. ",[19,20,21],"code",{},"context.Context"," — это именно тот механизм, который решает эту проблему.",[24,25],"hr",{},[27,28,30],"h2",{"id":29},"что-такое-context","Что такое Context",[15,32,33,36],{},[19,34,35],{},"Context"," — это интерфейс из четырёх методов:",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-go shiki shiki-themes github-dark","type Context interface {\n    Done() \u003C-chan struct{}      \u002F\u002F канал, закрывается при отмене\n    Err() error                \u002F\u002F причина отмены (nil если не отменён)\n    Deadline() (time.Time, bool) \u002F\u002F дедлайн если установлен\n    Value(key any) any         \u002F\u002F значение по ключу\n}\n","go","",[19,45,46,66,88,102,132,156],{"__ignoreMap":43},[47,48,51,55,59,62],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"snl16","type",[47,56,58],{"class":57},"svObZ"," Context",[47,60,61],{"class":53}," interface",[47,63,65],{"class":64},"s95oV"," {\n",[47,67,69,72,75,78,81,84],{"class":49,"line":68},2,[47,70,71],{"class":57},"    Done",[47,73,74],{"class":64},"() ",[47,76,77],{"class":53},"\u003C-chan",[47,79,80],{"class":53}," struct",[47,82,83],{"class":64},"{}      ",[47,85,87],{"class":86},"sAwPA","\u002F\u002F канал, закрывается при отмене\n",[47,89,91,94,96,99],{"class":49,"line":90},3,[47,92,93],{"class":57},"    Err",[47,95,74],{"class":64},[47,97,98],{"class":53},"error",[47,100,101],{"class":86},"                \u002F\u002F причина отмены (nil если не отменён)\n",[47,103,105,108,111,114,117,120,123,126,129],{"class":49,"line":104},4,[47,106,107],{"class":57},"    Deadline",[47,109,110],{"class":64},"() (",[47,112,113],{"class":57},"time",[47,115,116],{"class":64},".",[47,118,119],{"class":57},"Time",[47,121,122],{"class":64},", ",[47,124,125],{"class":53},"bool",[47,127,128],{"class":64},") ",[47,130,131],{"class":86},"\u002F\u002F дедлайн если установлен\n",[47,133,135,138,141,145,148,150,153],{"class":49,"line":134},5,[47,136,137],{"class":57},"    Value",[47,139,140],{"class":64},"(",[47,142,144],{"class":143},"s9osk","key",[47,146,147],{"class":57}," any",[47,149,128],{"class":64},[47,151,152],{"class":57},"any",[47,154,155],{"class":86},"         \u002F\u002F значение по ключу\n",[47,157,159],{"class":49,"line":158},6,[47,160,161],{"class":64},"}\n",[15,163,164,165,168],{},"Контекст всегда передаётся явно, первым аргументом, по соглашению называется ",[19,166,167],{},"ctx",":",[38,170,172],{"className":40,"code":171,"language":42,"meta":43,"style":43},"func ProcessOrder(ctx context.Context, orderID int) error {\n    user, err := fetchUser(ctx, orderID)\n    if err != nil {\n        return err\n    }\n    return saveResult(ctx, user)\n}\n",[19,173,174,207,221,238,246,251,262],{"__ignoreMap":43},[47,175,176,179,182,184,186,189,191,193,195,198,201,203,205],{"class":49,"line":50},[47,177,178],{"class":53},"func",[47,180,181],{"class":57}," ProcessOrder",[47,183,140],{"class":64},[47,185,167],{"class":143},[47,187,188],{"class":57}," context",[47,190,116],{"class":64},[47,192,35],{"class":57},[47,194,122],{"class":64},[47,196,197],{"class":143},"orderID",[47,199,200],{"class":53}," int",[47,202,128],{"class":64},[47,204,98],{"class":53},[47,206,65],{"class":64},[47,208,209,212,215,218],{"class":49,"line":68},[47,210,211],{"class":64},"    user, err ",[47,213,214],{"class":53},":=",[47,216,217],{"class":57}," fetchUser",[47,219,220],{"class":64},"(ctx, orderID)\n",[47,222,223,226,229,232,236],{"class":49,"line":90},[47,224,225],{"class":53},"    if",[47,227,228],{"class":64}," err ",[47,230,231],{"class":53},"!=",[47,233,235],{"class":234},"sDLfK"," nil",[47,237,65],{"class":64},[47,239,240,243],{"class":49,"line":104},[47,241,242],{"class":53},"        return",[47,244,245],{"class":64}," err\n",[47,247,248],{"class":49,"line":134},[47,249,250],{"class":64},"    }\n",[47,252,253,256,259],{"class":49,"line":158},[47,254,255],{"class":53},"    return",[47,257,258],{"class":57}," saveResult",[47,260,261],{"class":64},"(ctx, user)\n",[47,263,265],{"class":49,"line":264},7,[47,266,161],{"class":64},[15,268,269],{},"Это не просто соглашение — это архитектурное решение. Контекст явный, его видно в сигнатуре, и любой читатель кода понимает: функция поддерживает отмену и таймауты.",[24,271],{},[27,273,275],{"id":274},"дерево-контекстов","Дерево контекстов",[15,277,278],{},"Контексты образуют дерево: каждый дочерний контекст наследует свойства родителя. Отмена родителя автоматически отменяет всех потомков — в любую глубину:",[38,280,282],{"className":40,"code":281,"language":42,"meta":43,"style":43},"ctx := context.Background()          \u002F\u002F корень дерева\n\nctxA, cancelA := context.WithCancel(ctx)    \u002F\u002F дочерний A\nctxB, cancelB := context.WithCancel(ctxA)   \u002F\u002F дочерний B от A\nctxC, cancelC := context.WithCancel(ctxA)   \u002F\u002F дочерний C от A\n\ncancelA() \u002F\u002F отменяет A, B и C автоматически\n\u002F\u002F cancelB и cancelC теперь no-op — уже отменены\n",[19,283,284,303,309,327,344,360,364,374],{"__ignoreMap":43},[47,285,286,289,291,294,297,300],{"class":49,"line":50},[47,287,288],{"class":64},"ctx ",[47,290,214],{"class":53},[47,292,293],{"class":64}," context.",[47,295,296],{"class":57},"Background",[47,298,299],{"class":64},"()          ",[47,301,302],{"class":86},"\u002F\u002F корень дерева\n",[47,304,305],{"class":49,"line":68},[47,306,308],{"emptyLinePlaceholder":307},true,"\n",[47,310,311,314,316,318,321,324],{"class":49,"line":90},[47,312,313],{"class":64},"ctxA, cancelA ",[47,315,214],{"class":53},[47,317,293],{"class":64},[47,319,320],{"class":57},"WithCancel",[47,322,323],{"class":64},"(ctx)    ",[47,325,326],{"class":86},"\u002F\u002F дочерний A\n",[47,328,329,332,334,336,338,341],{"class":49,"line":104},[47,330,331],{"class":64},"ctxB, cancelB ",[47,333,214],{"class":53},[47,335,293],{"class":64},[47,337,320],{"class":57},[47,339,340],{"class":64},"(ctxA)   ",[47,342,343],{"class":86},"\u002F\u002F дочерний B от A\n",[47,345,346,349,351,353,355,357],{"class":49,"line":134},[47,347,348],{"class":64},"ctxC, cancelC ",[47,350,214],{"class":53},[47,352,293],{"class":64},[47,354,320],{"class":57},[47,356,340],{"class":64},[47,358,359],{"class":86},"\u002F\u002F дочерний C от A\n",[47,361,362],{"class":49,"line":158},[47,363,308],{"emptyLinePlaceholder":307},[47,365,366,369,371],{"class":49,"line":264},[47,367,368],{"class":57},"cancelA",[47,370,74],{"class":64},[47,372,373],{"class":86},"\u002F\u002F отменяет A, B и C автоматически\n",[47,375,377],{"class":49,"line":376},8,[47,378,379],{"class":86},"\u002F\u002F cancelB и cancelC теперь no-op — уже отменены\n",[15,381,382],{},"Это ключевое свойство: HTTP-сервер может отменить корневой контекст запроса, и все вложенные операции — запросы к БД, внешние вызовы, дочерние горутины — получат сигнал отмены автоматически.",[24,384],{},[27,386,388],{"id":387},"contextbackground-и-contexttodo","context.Background и context.TODO",[15,390,391],{},"Два корневых контекста, которые никогда не отменяются:",[38,393,395],{"className":40,"code":394,"language":42,"meta":43,"style":43},"\u002F\u002F Background — настоящий корень. Используется в main, тестах,\n\u002F\u002F инициализации и как корень для всех остальных контекстов\nctx := context.Background()\n\n\u002F\u002F TODO — заглушка. Используется когда контекст нужен по сигнатуре,\n\u002F\u002F но ещё не определено откуда он должен приходить\nctx := context.TODO()\n",[19,396,397,402,407,420,424,429,434],{"__ignoreMap":43},[47,398,399],{"class":49,"line":50},[47,400,401],{"class":86},"\u002F\u002F Background — настоящий корень. Используется в main, тестах,\n",[47,403,404],{"class":49,"line":68},[47,405,406],{"class":86},"\u002F\u002F инициализации и как корень для всех остальных контекстов\n",[47,408,409,411,413,415,417],{"class":49,"line":90},[47,410,288],{"class":64},[47,412,214],{"class":53},[47,414,293],{"class":64},[47,416,296],{"class":57},[47,418,419],{"class":64},"()\n",[47,421,422],{"class":49,"line":104},[47,423,308],{"emptyLinePlaceholder":307},[47,425,426],{"class":49,"line":134},[47,427,428],{"class":86},"\u002F\u002F TODO — заглушка. Используется когда контекст нужен по сигнатуре,\n",[47,430,431],{"class":49,"line":158},[47,432,433],{"class":86},"\u002F\u002F но ещё не определено откуда он должен приходить\n",[47,435,436,438,440,442,445],{"class":49,"line":264},[47,437,288],{"class":64},[47,439,214],{"class":53},[47,441,293],{"class":64},[47,443,444],{"class":57},"TODO",[47,446,419],{"class":64},[15,448,449,452,453,455],{},[19,450,451],{},"context.TODO()"," — это сигнал команде: \"здесь нужно разобраться с контекстом позже\". Линтеры умеют находить ",[19,454,444],{}," контексты в коде.",[24,457],{},[27,459,461],{"id":460},"withcancel-ручная-отмена","WithCancel — ручная отмена",[15,463,464,465,168],{},"Создаёт контекст с функцией отмены. Отмена происходит явным вызовом ",[19,466,467],{},"cancel",[38,469,472],{"className":40,"code":470,"language":42,"meta":471,"style":43},"func doLongWork(ctx context.Context) error {\n    time.Sleep(100 * time.Millisecond)\n    return nil\n}\n\nfunc fetchWithCancel() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel() \u002F\u002F всегда! даже если работа завершилась успешно\n\n    go func() {\n        \u002F\u002F имитируем отмену через секунду\n        time.Sleep(time.Second)\n        cancel()\n    }()\n\n    err := doLongWork(ctx)\n    if err != nil {\n        fmt.Println(err) \u002F\u002F context canceled\n    }\n}\n","static",[19,473,474,497,516,523,527,531,541,560,573,578,589,595,606,614,620,625,638,651,666,671],{"__ignoreMap":43},[47,475,476,478,481,483,485,487,489,491,493,495],{"class":49,"line":50},[47,477,178],{"class":53},[47,479,480],{"class":57}," doLongWork",[47,482,140],{"class":64},[47,484,167],{"class":143},[47,486,188],{"class":57},[47,488,116],{"class":64},[47,490,35],{"class":57},[47,492,128],{"class":64},[47,494,98],{"class":53},[47,496,65],{"class":64},[47,498,499,502,505,507,510,513],{"class":49,"line":68},[47,500,501],{"class":64},"    time.",[47,503,504],{"class":57},"Sleep",[47,506,140],{"class":64},[47,508,509],{"class":234},"100",[47,511,512],{"class":53}," *",[47,514,515],{"class":64}," time.Millisecond)\n",[47,517,518,520],{"class":49,"line":90},[47,519,255],{"class":53},[47,521,522],{"class":234}," nil\n",[47,524,525],{"class":49,"line":104},[47,526,161],{"class":64},[47,528,529],{"class":49,"line":134},[47,530,308],{"emptyLinePlaceholder":307},[47,532,533,535,538],{"class":49,"line":158},[47,534,178],{"class":53},[47,536,537],{"class":57}," fetchWithCancel",[47,539,540],{"class":64},"() {\n",[47,542,543,546,548,550,552,555,557],{"class":49,"line":264},[47,544,545],{"class":64},"    ctx, cancel ",[47,547,214],{"class":53},[47,549,293],{"class":64},[47,551,320],{"class":57},[47,553,554],{"class":64},"(context.",[47,556,296],{"class":57},[47,558,559],{"class":64},"())\n",[47,561,562,565,568,570],{"class":49,"line":376},[47,563,564],{"class":53},"    defer",[47,566,567],{"class":57}," cancel",[47,569,74],{"class":64},[47,571,572],{"class":86},"\u002F\u002F всегда! даже если работа завершилась успешно\n",[47,574,576],{"class":49,"line":575},9,[47,577,308],{"emptyLinePlaceholder":307},[47,579,581,584,587],{"class":49,"line":580},10,[47,582,583],{"class":53},"    go",[47,585,586],{"class":53}," func",[47,588,540],{"class":64},[47,590,592],{"class":49,"line":591},11,[47,593,594],{"class":86},"        \u002F\u002F имитируем отмену через секунду\n",[47,596,598,601,603],{"class":49,"line":597},12,[47,599,600],{"class":64},"        time.",[47,602,504],{"class":57},[47,604,605],{"class":64},"(time.Second)\n",[47,607,609,612],{"class":49,"line":608},13,[47,610,611],{"class":57},"        cancel",[47,613,419],{"class":64},[47,615,617],{"class":49,"line":616},14,[47,618,619],{"class":64},"    }()\n",[47,621,623],{"class":49,"line":622},15,[47,624,308],{"emptyLinePlaceholder":307},[47,626,628,631,633,635],{"class":49,"line":627},16,[47,629,630],{"class":64},"    err ",[47,632,214],{"class":53},[47,634,480],{"class":57},[47,636,637],{"class":64},"(ctx)\n",[47,639,641,643,645,647,649],{"class":49,"line":640},17,[47,642,225],{"class":53},[47,644,228],{"class":64},[47,646,231],{"class":53},[47,648,235],{"class":234},[47,650,65],{"class":64},[47,652,654,657,660,663],{"class":49,"line":653},18,[47,655,656],{"class":64},"        fmt.",[47,658,659],{"class":57},"Println",[47,661,662],{"class":64},"(err) ",[47,664,665],{"class":86},"\u002F\u002F context canceled\n",[47,667,669],{"class":49,"line":668},19,[47,670,250],{"class":64},[47,672,674],{"class":49,"line":673},20,[47,675,161],{"class":64},[15,677,678,681,682,684],{},[19,679,680],{},"defer cancel()"," — обязательное правило. Если не вызвать ",[19,683,467],{},", контекст и все его ресурсы не освободятся до отмены родителя. Это утечка — пусть небольшая, но в долгоживущих сервисах накапливается.",[686,687,689],"h3",{"id":688},"как-проверить-отмену-в-своём-коде","Как проверить отмену в своём коде",[38,691,693],{"className":40,"code":692,"language":42,"meta":43,"style":43},"func doLongWork(ctx context.Context) error {\n    for {\n        select {\n        case \u003C-ctx.Done():\n            return ctx.Err() \u002F\u002F context.Canceled или context.DeadlineExceeded\n        default:\n            \u002F\u002F продолжаем работу\n            if err := doStep(); err != nil {\n                return err\n            }\n        }\n    }\n}\n",[19,694,695,717,724,731,748,764,772,777,798,805,810,815,819],{"__ignoreMap":43},[47,696,697,699,701,703,705,707,709,711,713,715],{"class":49,"line":50},[47,698,178],{"class":53},[47,700,480],{"class":57},[47,702,140],{"class":64},[47,704,167],{"class":143},[47,706,188],{"class":57},[47,708,116],{"class":64},[47,710,35],{"class":57},[47,712,128],{"class":64},[47,714,98],{"class":53},[47,716,65],{"class":64},[47,718,719,722],{"class":49,"line":68},[47,720,721],{"class":53},"    for",[47,723,65],{"class":64},[47,725,726,729],{"class":49,"line":90},[47,727,728],{"class":53},"        select",[47,730,65],{"class":64},[47,732,733,736,739,742,745],{"class":49,"line":104},[47,734,735],{"class":53},"        case",[47,737,738],{"class":53}," \u003C-",[47,740,741],{"class":64},"ctx.",[47,743,744],{"class":57},"Done",[47,746,747],{"class":64},"():\n",[47,749,750,753,756,759,761],{"class":49,"line":134},[47,751,752],{"class":53},"            return",[47,754,755],{"class":64}," ctx.",[47,757,758],{"class":57},"Err",[47,760,74],{"class":64},[47,762,763],{"class":86},"\u002F\u002F context.Canceled или context.DeadlineExceeded\n",[47,765,766,769],{"class":49,"line":158},[47,767,768],{"class":53},"        default",[47,770,771],{"class":64},":\n",[47,773,774],{"class":49,"line":264},[47,775,776],{"class":86},"            \u002F\u002F продолжаем работу\n",[47,778,779,782,784,786,789,792,794,796],{"class":49,"line":376},[47,780,781],{"class":53},"            if",[47,783,228],{"class":64},[47,785,214],{"class":53},[47,787,788],{"class":57}," doStep",[47,790,791],{"class":64},"(); err ",[47,793,231],{"class":53},[47,795,235],{"class":234},[47,797,65],{"class":64},[47,799,800,803],{"class":49,"line":575},[47,801,802],{"class":53},"                return",[47,804,245],{"class":64},[47,806,807],{"class":49,"line":580},[47,808,809],{"class":64},"            }\n",[47,811,812],{"class":49,"line":591},[47,813,814],{"class":64},"        }\n",[47,816,817],{"class":49,"line":597},[47,818,250],{"class":64},[47,820,821],{"class":49,"line":608},[47,822,161],{"class":64},[15,824,825,826,829,830,833,834,837],{},"Паттерн ",[19,827,828],{},"select"," с ",[19,831,832],{},"ctx.Done()"," и ",[19,835,836],{},"default"," — стандартный способ встроить поддержку отмены в цикл. Если работа делается не в цикле, а линейно — проверяем перед каждой дорогой операцией:",[38,839,841],{"className":40,"code":840,"language":42,"meta":43,"style":43},"func processOrder(ctx context.Context, id int) error {\n    if err := ctx.Err(); err != nil {\n        return err \u002F\u002F быстрая проверка без select\n    }\n\n    user, err := db.GetUser(ctx, id) \u002F\u002F ctx передаём дальше\n    if err != nil {\n        return err\n    }\n\n    if err := ctx.Err(); err != nil { \u002F\u002F снова проверяем между операциями\n        return err\n    }\n\n    return sendEmail(ctx, user)\n}\n",[19,842,843,873,893,902,906,910,928,940,946,950,954,978,984,988,992,1001],{"__ignoreMap":43},[47,844,845,847,850,852,854,856,858,860,862,865,867,869,871],{"class":49,"line":50},[47,846,178],{"class":53},[47,848,849],{"class":57}," processOrder",[47,851,140],{"class":64},[47,853,167],{"class":143},[47,855,188],{"class":57},[47,857,116],{"class":64},[47,859,35],{"class":57},[47,861,122],{"class":64},[47,863,864],{"class":143},"id",[47,866,200],{"class":53},[47,868,128],{"class":64},[47,870,98],{"class":53},[47,872,65],{"class":64},[47,874,875,877,879,881,883,885,887,889,891],{"class":49,"line":68},[47,876,225],{"class":53},[47,878,228],{"class":64},[47,880,214],{"class":53},[47,882,755],{"class":64},[47,884,758],{"class":57},[47,886,791],{"class":64},[47,888,231],{"class":53},[47,890,235],{"class":234},[47,892,65],{"class":64},[47,894,895,897,899],{"class":49,"line":90},[47,896,242],{"class":53},[47,898,228],{"class":64},[47,900,901],{"class":86},"\u002F\u002F быстрая проверка без select\n",[47,903,904],{"class":49,"line":104},[47,905,250],{"class":64},[47,907,908],{"class":49,"line":134},[47,909,308],{"emptyLinePlaceholder":307},[47,911,912,914,916,919,922,925],{"class":49,"line":158},[47,913,211],{"class":64},[47,915,214],{"class":53},[47,917,918],{"class":64}," db.",[47,920,921],{"class":57},"GetUser",[47,923,924],{"class":64},"(ctx, id) ",[47,926,927],{"class":86},"\u002F\u002F ctx передаём дальше\n",[47,929,930,932,934,936,938],{"class":49,"line":264},[47,931,225],{"class":53},[47,933,228],{"class":64},[47,935,231],{"class":53},[47,937,235],{"class":234},[47,939,65],{"class":64},[47,941,942,944],{"class":49,"line":376},[47,943,242],{"class":53},[47,945,245],{"class":64},[47,947,948],{"class":49,"line":575},[47,949,250],{"class":64},[47,951,952],{"class":49,"line":580},[47,953,308],{"emptyLinePlaceholder":307},[47,955,956,958,960,962,964,966,968,970,972,975],{"class":49,"line":591},[47,957,225],{"class":53},[47,959,228],{"class":64},[47,961,214],{"class":53},[47,963,755],{"class":64},[47,965,758],{"class":57},[47,967,791],{"class":64},[47,969,231],{"class":53},[47,971,235],{"class":234},[47,973,974],{"class":64}," { ",[47,976,977],{"class":86},"\u002F\u002F снова проверяем между операциями\n",[47,979,980,982],{"class":49,"line":597},[47,981,242],{"class":53},[47,983,245],{"class":64},[47,985,986],{"class":49,"line":608},[47,987,250],{"class":64},[47,989,990],{"class":49,"line":616},[47,991,308],{"emptyLinePlaceholder":307},[47,993,994,996,999],{"class":49,"line":622},[47,995,255],{"class":53},[47,997,998],{"class":57}," sendEmail",[47,1000,261],{"class":64},[47,1002,1003],{"class":49,"line":627},[47,1004,161],{"class":64},[24,1006],{},[27,1008,1010],{"id":1009},"withtimeout-и-withdeadline","WithTimeout и WithDeadline",[15,1012,1013],{},"Оба создают контекст, который автоматически отменяется по времени. Разница только в способе задания момента отмены:",[38,1015,1017],{"className":40,"code":1016,"language":42,"meta":43,"style":43},"parent := context.Background()\n\n\u002F\u002F WithTimeout — через продолжительность от текущего момента\nctx, cancel := context.WithTimeout(parent, 5*time.Second)\ndefer cancel()\n_ = ctx\n\n\u002F\u002F WithDeadline — через конкретный момент времени\ndeadline := time.Now().Add(5 * time.Second)\nctx2, cancel2 := context.WithDeadline(parent, deadline)\ndefer cancel2()\n_ = ctx2\n",[19,1018,1019,1032,1036,1041,1065,1074,1085,1089,1094,1122,1137,1146],{"__ignoreMap":43},[47,1020,1021,1024,1026,1028,1030],{"class":49,"line":50},[47,1022,1023],{"class":64},"parent ",[47,1025,214],{"class":53},[47,1027,293],{"class":64},[47,1029,296],{"class":57},[47,1031,419],{"class":64},[47,1033,1034],{"class":49,"line":68},[47,1035,308],{"emptyLinePlaceholder":307},[47,1037,1038],{"class":49,"line":90},[47,1039,1040],{"class":86},"\u002F\u002F WithTimeout — через продолжительность от текущего момента\n",[47,1042,1043,1046,1048,1050,1053,1056,1059,1062],{"class":49,"line":104},[47,1044,1045],{"class":64},"ctx, cancel ",[47,1047,214],{"class":53},[47,1049,293],{"class":64},[47,1051,1052],{"class":57},"WithTimeout",[47,1054,1055],{"class":64},"(parent, ",[47,1057,1058],{"class":234},"5",[47,1060,1061],{"class":53},"*",[47,1063,1064],{"class":64},"time.Second)\n",[47,1066,1067,1070,1072],{"class":49,"line":134},[47,1068,1069],{"class":53},"defer",[47,1071,567],{"class":57},[47,1073,419],{"class":64},[47,1075,1076,1079,1082],{"class":49,"line":158},[47,1077,1078],{"class":64},"_ ",[47,1080,1081],{"class":53},"=",[47,1083,1084],{"class":64}," ctx\n",[47,1086,1087],{"class":49,"line":264},[47,1088,308],{"emptyLinePlaceholder":307},[47,1090,1091],{"class":49,"line":376},[47,1092,1093],{"class":86},"\u002F\u002F WithDeadline — через конкретный момент времени\n",[47,1095,1096,1099,1101,1104,1107,1110,1113,1115,1117,1119],{"class":49,"line":575},[47,1097,1098],{"class":64},"deadline ",[47,1100,214],{"class":53},[47,1102,1103],{"class":64}," time.",[47,1105,1106],{"class":57},"Now",[47,1108,1109],{"class":64},"().",[47,1111,1112],{"class":57},"Add",[47,1114,140],{"class":64},[47,1116,1058],{"class":234},[47,1118,512],{"class":53},[47,1120,1121],{"class":64}," time.Second)\n",[47,1123,1124,1127,1129,1131,1134],{"class":49,"line":580},[47,1125,1126],{"class":64},"ctx2, cancel2 ",[47,1128,214],{"class":53},[47,1130,293],{"class":64},[47,1132,1133],{"class":57},"WithDeadline",[47,1135,1136],{"class":64},"(parent, deadline)\n",[47,1138,1139,1141,1144],{"class":49,"line":591},[47,1140,1069],{"class":53},[47,1142,1143],{"class":57}," cancel2",[47,1145,419],{"class":64},[47,1147,1148,1150,1152],{"class":49,"line":597},[47,1149,1078],{"class":64},[47,1151,1081],{"class":53},[47,1153,1154],{"class":64}," ctx2\n",[15,1156,1157,1160,1161,116],{},[19,1158,1159],{},"WithTimeout(parent, d)"," — это просто синтаксический сахар над ",[19,1162,1163],{},"WithDeadline(parent, time.Now().Add(d))",[686,1165,1167],{"id":1166},"таймауты-в-http-клиенте","Таймауты в HTTP-клиенте",[15,1169,1170],{},"Самый распространённый сценарий — ограничить время на внешний вызов:",[38,1172,1174],{"className":40,"code":1173,"language":42,"meta":43,"style":43},"func callExternalAPI(url string) (*Response, error) {\n    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)\n    defer cancel()\n\n    req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)\n    if err != nil {\n        return nil, err\n    }\n\n    resp, err := http.DefaultClient.Do(req)\n    if err != nil {\n        \u002F\u002F если таймаут — err содержит context.DeadlineExceeded\n        return nil, fmt.Errorf(\"external API call failed: %w\", err)\n    }\n    defer resp.Body.Close()\n\n    \u002F\u002F ...\n}\n",[19,1175,1176,1206,1230,1238,1242,1264,1276,1285,1289,1293,1309,1321,1326,1353,1357,1369,1373,1378],{"__ignoreMap":43},[47,1177,1178,1180,1183,1185,1188,1191,1194,1196,1199,1201,1203],{"class":49,"line":50},[47,1179,178],{"class":53},[47,1181,1182],{"class":57}," callExternalAPI",[47,1184,140],{"class":64},[47,1186,1187],{"class":143},"url",[47,1189,1190],{"class":53}," string",[47,1192,1193],{"class":64},") (",[47,1195,1061],{"class":53},[47,1197,1198],{"class":57},"Response",[47,1200,122],{"class":64},[47,1202,98],{"class":53},[47,1204,1205],{"class":64},") {\n",[47,1207,1208,1210,1212,1214,1216,1218,1220,1223,1226,1228],{"class":49,"line":68},[47,1209,545],{"class":64},[47,1211,214],{"class":53},[47,1213,293],{"class":64},[47,1215,1052],{"class":57},[47,1217,554],{"class":64},[47,1219,296],{"class":57},[47,1221,1222],{"class":64},"(), ",[47,1224,1225],{"class":234},"3",[47,1227,1061],{"class":53},[47,1229,1064],{"class":64},[47,1231,1232,1234,1236],{"class":49,"line":90},[47,1233,564],{"class":53},[47,1235,567],{"class":57},[47,1237,419],{"class":64},[47,1239,1240],{"class":49,"line":104},[47,1241,308],{"emptyLinePlaceholder":307},[47,1243,1244,1247,1249,1252,1255,1258,1261],{"class":49,"line":134},[47,1245,1246],{"class":64},"    req, err ",[47,1248,214],{"class":53},[47,1250,1251],{"class":64}," http.",[47,1253,1254],{"class":57},"NewRequestWithContext",[47,1256,1257],{"class":64},"(ctx, http.MethodGet, url, ",[47,1259,1260],{"class":234},"nil",[47,1262,1263],{"class":64},")\n",[47,1265,1266,1268,1270,1272,1274],{"class":49,"line":158},[47,1267,225],{"class":53},[47,1269,228],{"class":64},[47,1271,231],{"class":53},[47,1273,235],{"class":234},[47,1275,65],{"class":64},[47,1277,1278,1280,1282],{"class":49,"line":264},[47,1279,242],{"class":53},[47,1281,235],{"class":234},[47,1283,1284],{"class":64},", err\n",[47,1286,1287],{"class":49,"line":376},[47,1288,250],{"class":64},[47,1290,1291],{"class":49,"line":575},[47,1292,308],{"emptyLinePlaceholder":307},[47,1294,1295,1298,1300,1303,1306],{"class":49,"line":580},[47,1296,1297],{"class":64},"    resp, err ",[47,1299,214],{"class":53},[47,1301,1302],{"class":64}," http.DefaultClient.",[47,1304,1305],{"class":57},"Do",[47,1307,1308],{"class":64},"(req)\n",[47,1310,1311,1313,1315,1317,1319],{"class":49,"line":591},[47,1312,225],{"class":53},[47,1314,228],{"class":64},[47,1316,231],{"class":53},[47,1318,235],{"class":234},[47,1320,65],{"class":64},[47,1322,1323],{"class":49,"line":597},[47,1324,1325],{"class":86},"        \u002F\u002F если таймаут — err содержит context.DeadlineExceeded\n",[47,1327,1328,1330,1332,1335,1338,1340,1344,1347,1350],{"class":49,"line":608},[47,1329,242],{"class":53},[47,1331,235],{"class":234},[47,1333,1334],{"class":64},", fmt.",[47,1336,1337],{"class":57},"Errorf",[47,1339,140],{"class":64},[47,1341,1343],{"class":1342},"sU2Wk","\"external API call failed: ",[47,1345,1346],{"class":234},"%w",[47,1348,1349],{"class":1342},"\"",[47,1351,1352],{"class":64},", err)\n",[47,1354,1355],{"class":49,"line":616},[47,1356,250],{"class":64},[47,1358,1359,1361,1364,1367],{"class":49,"line":622},[47,1360,564],{"class":53},[47,1362,1363],{"class":64}," resp.Body.",[47,1365,1366],{"class":57},"Close",[47,1368,419],{"class":64},[47,1370,1371],{"class":49,"line":627},[47,1372,308],{"emptyLinePlaceholder":307},[47,1374,1375],{"class":49,"line":640},[47,1376,1377],{"class":86},"    \u002F\u002F ...\n",[47,1379,1380],{"class":49,"line":653},[47,1381,161],{"class":64},[686,1383,1385],{"id":1384},"проверка-причины-отмены","Проверка причины отмены",[38,1387,1389],{"className":40,"code":1388,"language":42,"meta":471,"style":43},"func doWork(ctx context.Context) error {\n    time.Sleep(50 * time.Millisecond)\n    return nil\n}\n\nctx, cancel := context.WithTimeout(context.Background(), time.Second)\ndefer cancel()\n\nerr := doWork(ctx)\nif err != nil {\n    switch ctx.Err() {\n    case context.DeadlineExceeded:\n        fmt.Println(\"таймаут — операция заняла слишком долго\")\n    case context.Canceled:\n        fmt.Println(\"отменено явно через cancel()\")\n    }\n}\n",[19,1390,1391,1414,1429,1435,1439,1443,1460,1468,1472,1483,1496,1507,1515,1528,1535,1548,1552],{"__ignoreMap":43},[47,1392,1393,1395,1398,1400,1402,1404,1406,1408,1410,1412],{"class":49,"line":50},[47,1394,178],{"class":53},[47,1396,1397],{"class":57}," doWork",[47,1399,140],{"class":64},[47,1401,167],{"class":143},[47,1403,188],{"class":57},[47,1405,116],{"class":64},[47,1407,35],{"class":57},[47,1409,128],{"class":64},[47,1411,98],{"class":53},[47,1413,65],{"class":64},[47,1415,1416,1418,1420,1422,1425,1427],{"class":49,"line":68},[47,1417,501],{"class":64},[47,1419,504],{"class":57},[47,1421,140],{"class":64},[47,1423,1424],{"class":234},"50",[47,1426,512],{"class":53},[47,1428,515],{"class":64},[47,1430,1431,1433],{"class":49,"line":90},[47,1432,255],{"class":53},[47,1434,522],{"class":234},[47,1436,1437],{"class":49,"line":104},[47,1438,161],{"class":64},[47,1440,1441],{"class":49,"line":134},[47,1442,308],{"emptyLinePlaceholder":307},[47,1444,1445,1447,1449,1451,1453,1455,1457],{"class":49,"line":158},[47,1446,1045],{"class":64},[47,1448,214],{"class":53},[47,1450,293],{"class":64},[47,1452,1052],{"class":57},[47,1454,554],{"class":64},[47,1456,296],{"class":57},[47,1458,1459],{"class":64},"(), time.Second)\n",[47,1461,1462,1464,1466],{"class":49,"line":264},[47,1463,1069],{"class":53},[47,1465,567],{"class":57},[47,1467,419],{"class":64},[47,1469,1470],{"class":49,"line":376},[47,1471,308],{"emptyLinePlaceholder":307},[47,1473,1474,1477,1479,1481],{"class":49,"line":575},[47,1475,1476],{"class":64},"err ",[47,1478,214],{"class":53},[47,1480,1397],{"class":57},[47,1482,637],{"class":64},[47,1484,1485,1488,1490,1492,1494],{"class":49,"line":580},[47,1486,1487],{"class":53},"if",[47,1489,228],{"class":64},[47,1491,231],{"class":53},[47,1493,235],{"class":234},[47,1495,65],{"class":64},[47,1497,1498,1501,1503,1505],{"class":49,"line":591},[47,1499,1500],{"class":53},"    switch",[47,1502,755],{"class":64},[47,1504,758],{"class":57},[47,1506,540],{"class":64},[47,1508,1509,1512],{"class":49,"line":597},[47,1510,1511],{"class":53},"    case",[47,1513,1514],{"class":64}," context.DeadlineExceeded:\n",[47,1516,1517,1519,1521,1523,1526],{"class":49,"line":608},[47,1518,656],{"class":64},[47,1520,659],{"class":57},[47,1522,140],{"class":64},[47,1524,1525],{"class":1342},"\"таймаут — операция заняла слишком долго\"",[47,1527,1263],{"class":64},[47,1529,1530,1532],{"class":49,"line":616},[47,1531,1511],{"class":53},[47,1533,1534],{"class":64}," context.Canceled:\n",[47,1536,1537,1539,1541,1543,1546],{"class":49,"line":622},[47,1538,656],{"class":64},[47,1540,659],{"class":57},[47,1542,140],{"class":64},[47,1544,1545],{"class":1342},"\"отменено явно через cancel()\"",[47,1547,1263],{"class":64},[47,1549,1550],{"class":49,"line":627},[47,1551,250],{"class":64},[47,1553,1554],{"class":49,"line":640},[47,1555,161],{"class":64},[686,1557,1559],{"id":1558},"дедлайн-из-родителя-наследуется","Дедлайн из родителя наследуется",[15,1561,1562],{},"Если родительский контекст уже имеет дедлайн — дочерний не может его расширить, только сузить:",[38,1564,1566],{"className":40,"code":1565,"language":42,"meta":43,"style":43},"\u002F\u002F Родитель с дедлайном через 2 секунды\nparent, cancel := context.WithTimeout(context.Background(), 2*time.Second)\ndefer cancel()\n\n\u002F\u002F Попытка дать дочернему 10 секунд — бессмысленно,\n\u002F\u002F родитель отменится через 2 секунды и дочерний тоже\nchild, cancel2 := context.WithTimeout(parent, 10*time.Second)\ndefer cancel2()\n\n\u002F\u002F Дочерний с меньшим таймаутом — имеет смысл\nchild2, cancel3 := context.WithTimeout(parent, 500*time.Millisecond)\ndefer cancel3()\n",[19,1567,1568,1573,1597,1605,1609,1614,1619,1639,1647,1651,1656,1677],{"__ignoreMap":43},[47,1569,1570],{"class":49,"line":50},[47,1571,1572],{"class":86},"\u002F\u002F Родитель с дедлайном через 2 секунды\n",[47,1574,1575,1578,1580,1582,1584,1586,1588,1590,1593,1595],{"class":49,"line":68},[47,1576,1577],{"class":64},"parent, cancel ",[47,1579,214],{"class":53},[47,1581,293],{"class":64},[47,1583,1052],{"class":57},[47,1585,554],{"class":64},[47,1587,296],{"class":57},[47,1589,1222],{"class":64},[47,1591,1592],{"class":234},"2",[47,1594,1061],{"class":53},[47,1596,1064],{"class":64},[47,1598,1599,1601,1603],{"class":49,"line":90},[47,1600,1069],{"class":53},[47,1602,567],{"class":57},[47,1604,419],{"class":64},[47,1606,1607],{"class":49,"line":104},[47,1608,308],{"emptyLinePlaceholder":307},[47,1610,1611],{"class":49,"line":134},[47,1612,1613],{"class":86},"\u002F\u002F Попытка дать дочернему 10 секунд — бессмысленно,\n",[47,1615,1616],{"class":49,"line":158},[47,1617,1618],{"class":86},"\u002F\u002F родитель отменится через 2 секунды и дочерний тоже\n",[47,1620,1621,1624,1626,1628,1630,1632,1635,1637],{"class":49,"line":264},[47,1622,1623],{"class":64},"child, cancel2 ",[47,1625,214],{"class":53},[47,1627,293],{"class":64},[47,1629,1052],{"class":57},[47,1631,1055],{"class":64},[47,1633,1634],{"class":234},"10",[47,1636,1061],{"class":53},[47,1638,1064],{"class":64},[47,1640,1641,1643,1645],{"class":49,"line":376},[47,1642,1069],{"class":53},[47,1644,1143],{"class":57},[47,1646,419],{"class":64},[47,1648,1649],{"class":49,"line":575},[47,1650,308],{"emptyLinePlaceholder":307},[47,1652,1653],{"class":49,"line":580},[47,1654,1655],{"class":86},"\u002F\u002F Дочерний с меньшим таймаутом — имеет смысл\n",[47,1657,1658,1661,1663,1665,1667,1669,1672,1674],{"class":49,"line":591},[47,1659,1660],{"class":64},"child2, cancel3 ",[47,1662,214],{"class":53},[47,1664,293],{"class":64},[47,1666,1052],{"class":57},[47,1668,1055],{"class":64},[47,1670,1671],{"class":234},"500",[47,1673,1061],{"class":53},[47,1675,1676],{"class":64},"time.Millisecond)\n",[47,1678,1679,1681,1684],{"class":49,"line":597},[47,1680,1069],{"class":53},[47,1682,1683],{"class":57}," cancel3",[47,1685,419],{"class":64},[24,1687],{},[27,1689,1691],{"id":1690},"withvalue-передача-значений","WithValue — передача значений",[15,1693,1694,1697,1698,1701],{},[19,1695,1696],{},"WithValue"," создаёт контекст, несущий пару ключ-значение. Значение доступно через ",[19,1699,1700],{},"ctx.Value(key)"," в любом дочернем контексте:",[38,1703,1705],{"className":40,"code":1704,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\ntype contextKey string \u002F\u002F собственный тип ключа\n\nconst (\n    requestIDKey contextKey = \"requestID\"\n    userIDKey    contextKey = \"userID\"\n)\n\nfunc handler(ctx context.Context) {\n    reqID, ok := ctx.Value(requestIDKey).(string)\n    if !ok {\n        reqID = \"unknown\"\n    }\n    fmt.Println(\"request:\", reqID)\n}\n\nfunc main() {\n    parent := context.Background()\n    ctx := context.WithValue(parent, requestIDKey, \"abc-123\")\n    _ = userIDKey \u002F\u002F показано выше как пример\n    handler(ctx)\n}\n",[19,1706,1707,1715,1719,1727,1738,1747,1751,1755,1767,1771,1778,1791,1804,1808,1812,1831,1851,1861,1871,1875,1890,1895,1900,1910,1924,1944,1958,1966],{"__ignoreMap":43},[47,1708,1709,1712],{"class":49,"line":50},[47,1710,1711],{"class":53},"package",[47,1713,1714],{"class":57}," main\n",[47,1716,1717],{"class":49,"line":68},[47,1718,308],{"emptyLinePlaceholder":307},[47,1720,1721,1724],{"class":49,"line":90},[47,1722,1723],{"class":53},"import",[47,1725,1726],{"class":64}," (\n",[47,1728,1729,1732,1735],{"class":49,"line":104},[47,1730,1731],{"class":1342},"    \"",[47,1733,1734],{"class":57},"context",[47,1736,1737],{"class":1342},"\"\n",[47,1739,1740,1742,1745],{"class":49,"line":134},[47,1741,1731],{"class":1342},[47,1743,1744],{"class":57},"fmt",[47,1746,1737],{"class":1342},[47,1748,1749],{"class":49,"line":158},[47,1750,1263],{"class":64},[47,1752,1753],{"class":49,"line":264},[47,1754,308],{"emptyLinePlaceholder":307},[47,1756,1757,1759,1762,1764],{"class":49,"line":376},[47,1758,54],{"class":53},[47,1760,1761],{"class":57}," contextKey",[47,1763,1190],{"class":53},[47,1765,1766],{"class":86}," \u002F\u002F собственный тип ключа\n",[47,1768,1769],{"class":49,"line":575},[47,1770,308],{"emptyLinePlaceholder":307},[47,1772,1773,1776],{"class":49,"line":580},[47,1774,1775],{"class":53},"const",[47,1777,1726],{"class":64},[47,1779,1780,1783,1785,1788],{"class":49,"line":591},[47,1781,1782],{"class":234},"    requestIDKey",[47,1784,1761],{"class":57},[47,1786,1787],{"class":53}," =",[47,1789,1790],{"class":1342}," \"requestID\"\n",[47,1792,1793,1796,1799,1801],{"class":49,"line":597},[47,1794,1795],{"class":234},"    userIDKey",[47,1797,1798],{"class":57},"    contextKey",[47,1800,1787],{"class":53},[47,1802,1803],{"class":1342}," \"userID\"\n",[47,1805,1806],{"class":49,"line":608},[47,1807,1263],{"class":64},[47,1809,1810],{"class":49,"line":616},[47,1811,308],{"emptyLinePlaceholder":307},[47,1813,1814,1816,1819,1821,1823,1825,1827,1829],{"class":49,"line":622},[47,1815,178],{"class":53},[47,1817,1818],{"class":57}," handler",[47,1820,140],{"class":64},[47,1822,167],{"class":143},[47,1824,188],{"class":57},[47,1826,116],{"class":64},[47,1828,35],{"class":57},[47,1830,1205],{"class":64},[47,1832,1833,1836,1838,1840,1843,1846,1849],{"class":49,"line":627},[47,1834,1835],{"class":64},"    reqID, ok ",[47,1837,214],{"class":53},[47,1839,755],{"class":64},[47,1841,1842],{"class":57},"Value",[47,1844,1845],{"class":64},"(requestIDKey).(",[47,1847,1848],{"class":53},"string",[47,1850,1263],{"class":64},[47,1852,1853,1855,1858],{"class":49,"line":640},[47,1854,225],{"class":53},[47,1856,1857],{"class":53}," !",[47,1859,1860],{"class":64},"ok {\n",[47,1862,1863,1866,1868],{"class":49,"line":653},[47,1864,1865],{"class":64},"        reqID ",[47,1867,1081],{"class":53},[47,1869,1870],{"class":1342}," \"unknown\"\n",[47,1872,1873],{"class":49,"line":668},[47,1874,250],{"class":64},[47,1876,1877,1880,1882,1884,1887],{"class":49,"line":673},[47,1878,1879],{"class":64},"    fmt.",[47,1881,659],{"class":57},[47,1883,140],{"class":64},[47,1885,1886],{"class":1342},"\"request:\"",[47,1888,1889],{"class":64},", reqID)\n",[47,1891,1893],{"class":49,"line":1892},21,[47,1894,161],{"class":64},[47,1896,1898],{"class":49,"line":1897},22,[47,1899,308],{"emptyLinePlaceholder":307},[47,1901,1903,1905,1908],{"class":49,"line":1902},23,[47,1904,178],{"class":53},[47,1906,1907],{"class":57}," main",[47,1909,540],{"class":64},[47,1911,1913,1916,1918,1920,1922],{"class":49,"line":1912},24,[47,1914,1915],{"class":64},"    parent ",[47,1917,214],{"class":53},[47,1919,293],{"class":64},[47,1921,296],{"class":57},[47,1923,419],{"class":64},[47,1925,1927,1930,1932,1934,1936,1939,1942],{"class":49,"line":1926},25,[47,1928,1929],{"class":64},"    ctx ",[47,1931,214],{"class":53},[47,1933,293],{"class":64},[47,1935,1696],{"class":57},[47,1937,1938],{"class":64},"(parent, requestIDKey, ",[47,1940,1941],{"class":1342},"\"abc-123\"",[47,1943,1263],{"class":64},[47,1945,1947,1950,1952,1955],{"class":49,"line":1946},26,[47,1948,1949],{"class":64},"    _ ",[47,1951,1081],{"class":53},[47,1953,1954],{"class":64}," userIDKey ",[47,1956,1957],{"class":86},"\u002F\u002F показано выше как пример\n",[47,1959,1961,1964],{"class":49,"line":1960},27,[47,1962,1963],{"class":57},"    handler",[47,1965,637],{"class":64},[47,1967,1969],{"class":49,"line":1968},28,[47,1970,161],{"class":64},[686,1972,1974],{"id":1973},"почему-ключ-должен-быть-собственным-типом","Почему ключ должен быть собственным типом",[15,1976,1977],{},"Контекст глобален для цепочки вызовов, и коллизии ключей реальны. Если использовать строку как ключ напрямую — любой пакет может случайно перезаписать значение:",[38,1979,1981],{"className":40,"code":1980,"language":42,"meta":43,"style":43},"\u002F\u002F Плохо: строка как ключ — коллизии между пакетами\nctx = context.WithValue(ctx, \"userID\", 42)\nctx = context.WithValue(ctx, \"userID\", 99) \u002F\u002F перезаписали!\n\n\u002F\u002F Хорошо: собственный тип — гарантированно уникален\ntype myPkg struct{ key string }\nctx = context.WithValue(ctx, myPkg{\"userID\"}, 42)\n",[19,1982,1983,1988,2011,2035,2039,2044,2061],{"__ignoreMap":43},[47,1984,1985],{"class":49,"line":50},[47,1986,1987],{"class":86},"\u002F\u002F Плохо: строка как ключ — коллизии между пакетами\n",[47,1989,1990,1992,1994,1996,1998,2001,2004,2006,2009],{"class":49,"line":68},[47,1991,288],{"class":64},[47,1993,1081],{"class":53},[47,1995,293],{"class":64},[47,1997,1696],{"class":57},[47,1999,2000],{"class":64},"(ctx, ",[47,2002,2003],{"class":1342},"\"userID\"",[47,2005,122],{"class":64},[47,2007,2008],{"class":234},"42",[47,2010,1263],{"class":64},[47,2012,2013,2015,2017,2019,2021,2023,2025,2027,2030,2032],{"class":49,"line":90},[47,2014,288],{"class":64},[47,2016,1081],{"class":53},[47,2018,293],{"class":64},[47,2020,1696],{"class":57},[47,2022,2000],{"class":64},[47,2024,2003],{"class":1342},[47,2026,122],{"class":64},[47,2028,2029],{"class":234},"99",[47,2031,128],{"class":64},[47,2033,2034],{"class":86},"\u002F\u002F перезаписали!\n",[47,2036,2037],{"class":49,"line":104},[47,2038,308],{"emptyLinePlaceholder":307},[47,2040,2041],{"class":49,"line":134},[47,2042,2043],{"class":86},"\u002F\u002F Хорошо: собственный тип — гарантированно уникален\n",[47,2045,2046,2048,2051,2053,2056,2058],{"class":49,"line":158},[47,2047,54],{"class":53},[47,2049,2050],{"class":57}," myPkg",[47,2052,80],{"class":53},[47,2054,2055],{"class":64},"{ key ",[47,2057,1848],{"class":53},[47,2059,2060],{"class":64}," }\n",[47,2062,2063,2065,2067,2069,2071,2073,2076,2079,2081,2084,2086],{"class":49,"line":264},[47,2064,288],{"class":64},[47,2066,1081],{"class":53},[47,2068,293],{"class":64},[47,2070,1696],{"class":57},[47,2072,2000],{"class":64},[47,2074,2075],{"class":57},"myPkg",[47,2077,2078],{"class":64},"{",[47,2080,2003],{"class":1342},[47,2082,2083],{"class":64},"}, ",[47,2085,2008],{"class":234},[47,2087,1263],{"class":64},[15,2089,2090],{},"Собственный неэкспортируемый тип ключа — единственный надёжный способ избежать коллизий.",[686,2092,2094],{"id":2093},"что-передавать-через-контекст","Что передавать через контекст",[15,2096,2097,2098,2102],{},"Context — не замена аргументам функции. Через него передают ",[2099,2100,2101],"strong",{},"cross-cutting concerns"," — данные, сквозные для всего запроса:",[38,2104,2106],{"className":40,"code":2105,"language":42,"meta":43,"style":43},"\u002F\u002F Хорошо: через контекст\n\u002F\u002F - Request ID \u002F Trace ID для логирования и трейсинга\n\u002F\u002F - User ID аутентифицированного пользователя\n\u002F\u002F - Tenant ID в мультитенантных системах\n\u002F\u002F - Deadline \u002F таймаут\n\n\u002F\u002F Плохо: через контекст\n\u002F\u002F - Параметры бизнес-логики (передавайте явными аргументами)\n\u002F\u002F - Опциональные зависимости (инъектируйте через конструктор)\n\u002F\u002F - Ошибки (возвращайте явно)\n",[19,2107,2108,2113,2118,2123,2128,2133,2137,2142,2147,2152],{"__ignoreMap":43},[47,2109,2110],{"class":49,"line":50},[47,2111,2112],{"class":86},"\u002F\u002F Хорошо: через контекст\n",[47,2114,2115],{"class":49,"line":68},[47,2116,2117],{"class":86},"\u002F\u002F - Request ID \u002F Trace ID для логирования и трейсинга\n",[47,2119,2120],{"class":49,"line":90},[47,2121,2122],{"class":86},"\u002F\u002F - User ID аутентифицированного пользователя\n",[47,2124,2125],{"class":49,"line":104},[47,2126,2127],{"class":86},"\u002F\u002F - Tenant ID в мультитенантных системах\n",[47,2129,2130],{"class":49,"line":134},[47,2131,2132],{"class":86},"\u002F\u002F - Deadline \u002F таймаут\n",[47,2134,2135],{"class":49,"line":158},[47,2136,308],{"emptyLinePlaceholder":307},[47,2138,2139],{"class":49,"line":264},[47,2140,2141],{"class":86},"\u002F\u002F Плохо: через контекст\n",[47,2143,2144],{"class":49,"line":376},[47,2145,2146],{"class":86},"\u002F\u002F - Параметры бизнес-логики (передавайте явными аргументами)\n",[47,2148,2149],{"class":49,"line":575},[47,2150,2151],{"class":86},"\u002F\u002F - Опциональные зависимости (инъектируйте через конструктор)\n",[47,2153,2154],{"class":49,"line":580},[47,2155,2156],{"class":86},"\u002F\u002F - Ошибки (возвращайте явно)\n",[15,2158,2159],{},"Если что-то нужно везде в цепочке вызовов запроса — контекст. Если что-то нужно конкретной функции — явный аргумент.",[24,2161],{},[27,2163,2165],{"id":2164},"context-в-http-сервере","Context в HTTP-сервере",[15,2167,2168],{},"Стандартная библиотека уже интегрирует Context: каждый входящий запрос несёт контекст, который отменяется когда клиент разрывает соединение:",[38,2170,2172],{"className":40,"code":2171,"language":42,"meta":43,"style":43},"func orderHandler(w http.ResponseWriter, r *http.Request) {\n    ctx := r.Context() \u002F\u002F контекст запроса — отменится если клиент ушёл\n\n    \u002F\u002F Добавляем request ID для трейсинга\n    reqID := r.Header.Get(\"X-Request-ID\")\n    ctx = context.WithValue(ctx, requestIDKey, reqID)\n\n    \u002F\u002F Ограничиваем время обработки\n    ctx, cancel := context.WithTimeout(ctx, 10*time.Second)\n    defer cancel()\n\n    result, err := processOrder(ctx, r)\n    if err != nil {\n        if errors.Is(err, context.Canceled) {\n            \u002F\u002F клиент ушёл — не нужно возвращать ошибку\n            return\n        }\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n    }\n\n    json.NewEncoder(w).Encode(result)\n}\n",[19,2173,2174,2211,2227,2231,2236,2256,2269,2273,2278,2296,2304,2308,2320,2332,2346,2351,2356,2360,2376,2381,2385,2389,2406],{"__ignoreMap":43},[47,2175,2176,2178,2181,2183,2186,2189,2191,2194,2196,2199,2201,2204,2206,2209],{"class":49,"line":50},[47,2177,178],{"class":53},[47,2179,2180],{"class":57}," orderHandler",[47,2182,140],{"class":64},[47,2184,2185],{"class":143},"w",[47,2187,2188],{"class":57}," http",[47,2190,116],{"class":64},[47,2192,2193],{"class":57},"ResponseWriter",[47,2195,122],{"class":64},[47,2197,2198],{"class":143},"r",[47,2200,512],{"class":53},[47,2202,2203],{"class":57},"http",[47,2205,116],{"class":64},[47,2207,2208],{"class":57},"Request",[47,2210,1205],{"class":64},[47,2212,2213,2215,2217,2220,2222,2224],{"class":49,"line":68},[47,2214,1929],{"class":64},[47,2216,214],{"class":53},[47,2218,2219],{"class":64}," r.",[47,2221,35],{"class":57},[47,2223,74],{"class":64},[47,2225,2226],{"class":86},"\u002F\u002F контекст запроса — отменится если клиент ушёл\n",[47,2228,2229],{"class":49,"line":90},[47,2230,308],{"emptyLinePlaceholder":307},[47,2232,2233],{"class":49,"line":104},[47,2234,2235],{"class":86},"    \u002F\u002F Добавляем request ID для трейсинга\n",[47,2237,2238,2241,2243,2246,2249,2251,2254],{"class":49,"line":134},[47,2239,2240],{"class":64},"    reqID ",[47,2242,214],{"class":53},[47,2244,2245],{"class":64}," r.Header.",[47,2247,2248],{"class":57},"Get",[47,2250,140],{"class":64},[47,2252,2253],{"class":1342},"\"X-Request-ID\"",[47,2255,1263],{"class":64},[47,2257,2258,2260,2262,2264,2266],{"class":49,"line":158},[47,2259,1929],{"class":64},[47,2261,1081],{"class":53},[47,2263,293],{"class":64},[47,2265,1696],{"class":57},[47,2267,2268],{"class":64},"(ctx, requestIDKey, reqID)\n",[47,2270,2271],{"class":49,"line":264},[47,2272,308],{"emptyLinePlaceholder":307},[47,2274,2275],{"class":49,"line":376},[47,2276,2277],{"class":86},"    \u002F\u002F Ограничиваем время обработки\n",[47,2279,2280,2282,2284,2286,2288,2290,2292,2294],{"class":49,"line":575},[47,2281,545],{"class":64},[47,2283,214],{"class":53},[47,2285,293],{"class":64},[47,2287,1052],{"class":57},[47,2289,2000],{"class":64},[47,2291,1634],{"class":234},[47,2293,1061],{"class":53},[47,2295,1064],{"class":64},[47,2297,2298,2300,2302],{"class":49,"line":580},[47,2299,564],{"class":53},[47,2301,567],{"class":57},[47,2303,419],{"class":64},[47,2305,2306],{"class":49,"line":591},[47,2307,308],{"emptyLinePlaceholder":307},[47,2309,2310,2313,2315,2317],{"class":49,"line":597},[47,2311,2312],{"class":64},"    result, err ",[47,2314,214],{"class":53},[47,2316,849],{"class":57},[47,2318,2319],{"class":64},"(ctx, r)\n",[47,2321,2322,2324,2326,2328,2330],{"class":49,"line":608},[47,2323,225],{"class":53},[47,2325,228],{"class":64},[47,2327,231],{"class":53},[47,2329,235],{"class":234},[47,2331,65],{"class":64},[47,2333,2334,2337,2340,2343],{"class":49,"line":616},[47,2335,2336],{"class":53},"        if",[47,2338,2339],{"class":64}," errors.",[47,2341,2342],{"class":57},"Is",[47,2344,2345],{"class":64},"(err, context.Canceled) {\n",[47,2347,2348],{"class":49,"line":622},[47,2349,2350],{"class":86},"            \u002F\u002F клиент ушёл — не нужно возвращать ошибку\n",[47,2352,2353],{"class":49,"line":627},[47,2354,2355],{"class":53},"            return\n",[47,2357,2358],{"class":49,"line":640},[47,2359,814],{"class":64},[47,2361,2362,2365,2368,2371,2373],{"class":49,"line":653},[47,2363,2364],{"class":64},"        http.",[47,2366,2367],{"class":57},"Error",[47,2369,2370],{"class":64},"(w, err.",[47,2372,2367],{"class":57},[47,2374,2375],{"class":64},"(), http.StatusInternalServerError)\n",[47,2377,2378],{"class":49,"line":668},[47,2379,2380],{"class":53},"        return\n",[47,2382,2383],{"class":49,"line":673},[47,2384,250],{"class":64},[47,2386,2387],{"class":49,"line":1892},[47,2388,308],{"emptyLinePlaceholder":307},[47,2390,2391,2394,2397,2400,2403],{"class":49,"line":1897},[47,2392,2393],{"class":64},"    json.",[47,2395,2396],{"class":57},"NewEncoder",[47,2398,2399],{"class":64},"(w).",[47,2401,2402],{"class":57},"Encode",[47,2404,2405],{"class":64},"(result)\n",[47,2407,2408],{"class":49,"line":1902},[47,2409,161],{"class":64},[24,2411],{},[27,2413,2415],{"id":2414},"распространённые-ошибки","Распространённые ошибки",[686,2417,2419],{"id":2418},"хранение-контекста-в-структуре","Хранение контекста в структуре",[38,2421,2423],{"className":40,"code":2422,"language":42,"meta":43,"style":43},"\u002F\u002F Плохо: контекст в структуре\ntype Service struct {\n    ctx context.Context \u002F\u002F антипаттерн\n    db  *sql.DB\n}\n\n\u002F\u002F Хорошо: контекст через аргумент метода\ntype Service struct {\n    db *sql.DB\n}\n\nfunc (s *Service) Process(ctx context.Context, id int) error {\n    return s.db.QueryRowContext(ctx, \"SELECT ...\", id)\n}\n",[19,2424,2425,2430,2441,2454,2469,2473,2477,2482,2492,2505,2509,2513,2555,2573],{"__ignoreMap":43},[47,2426,2427],{"class":49,"line":50},[47,2428,2429],{"class":86},"\u002F\u002F Плохо: контекст в структуре\n",[47,2431,2432,2434,2437,2439],{"class":49,"line":68},[47,2433,54],{"class":53},[47,2435,2436],{"class":57}," Service",[47,2438,80],{"class":53},[47,2440,65],{"class":64},[47,2442,2443,2445,2447,2449,2451],{"class":49,"line":90},[47,2444,1929],{"class":64},[47,2446,1734],{"class":57},[47,2448,116],{"class":64},[47,2450,35],{"class":57},[47,2452,2453],{"class":86}," \u002F\u002F антипаттерн\n",[47,2455,2456,2459,2461,2464,2466],{"class":49,"line":104},[47,2457,2458],{"class":64},"    db  ",[47,2460,1061],{"class":53},[47,2462,2463],{"class":57},"sql",[47,2465,116],{"class":64},[47,2467,2468],{"class":57},"DB\n",[47,2470,2471],{"class":49,"line":134},[47,2472,161],{"class":64},[47,2474,2475],{"class":49,"line":158},[47,2476,308],{"emptyLinePlaceholder":307},[47,2478,2479],{"class":49,"line":264},[47,2480,2481],{"class":86},"\u002F\u002F Хорошо: контекст через аргумент метода\n",[47,2483,2484,2486,2488,2490],{"class":49,"line":376},[47,2485,54],{"class":53},[47,2487,2436],{"class":57},[47,2489,80],{"class":53},[47,2491,65],{"class":64},[47,2493,2494,2497,2499,2501,2503],{"class":49,"line":575},[47,2495,2496],{"class":64},"    db ",[47,2498,1061],{"class":53},[47,2500,2463],{"class":57},[47,2502,116],{"class":64},[47,2504,2468],{"class":57},[47,2506,2507],{"class":49,"line":580},[47,2508,161],{"class":64},[47,2510,2511],{"class":49,"line":591},[47,2512,308],{"emptyLinePlaceholder":307},[47,2514,2515,2517,2520,2523,2525,2528,2530,2533,2535,2537,2539,2541,2543,2545,2547,2549,2551,2553],{"class":49,"line":597},[47,2516,178],{"class":53},[47,2518,2519],{"class":64}," (",[47,2521,2522],{"class":143},"s ",[47,2524,1061],{"class":53},[47,2526,2527],{"class":57},"Service",[47,2529,128],{"class":64},[47,2531,2532],{"class":57},"Process",[47,2534,140],{"class":64},[47,2536,167],{"class":143},[47,2538,188],{"class":57},[47,2540,116],{"class":64},[47,2542,35],{"class":57},[47,2544,122],{"class":64},[47,2546,864],{"class":143},[47,2548,200],{"class":53},[47,2550,128],{"class":64},[47,2552,98],{"class":53},[47,2554,65],{"class":64},[47,2556,2557,2559,2562,2565,2567,2570],{"class":49,"line":608},[47,2558,255],{"class":53},[47,2560,2561],{"class":64}," s.db.",[47,2563,2564],{"class":57},"QueryRowContext",[47,2566,2000],{"class":64},[47,2568,2569],{"class":1342},"\"SELECT ...\"",[47,2571,2572],{"class":64},", id)\n",[47,2574,2575],{"class":49,"line":616},[47,2576,161],{"class":64},[15,2578,2579],{},"Контекст привязан к конкретному запросу и живёт столько, сколько живёт запрос. Структура может жить дольше — хранить в ней контекст означает держать устаревший или уже отменённый контекст.",[686,2581,2583],{"id":2582},"передача-nil-вместо-контекста","Передача nil вместо контекста",[38,2585,2587],{"className":40,"code":2586,"language":42,"meta":43,"style":43},"\u002F\u002F Плохо: nil вызовет панику внутри при обращении к ctx.Done()\nprocessOrder(nil, orderID)\n\n\u002F\u002F Хорошо: если нет контекста — используй Background или TODO\nprocessOrder(context.Background(), orderID)\nprocessOrder(context.TODO(), orderID) \u002F\u002F если контекст появится позже\n",[19,2588,2589,2594,2606,2610,2615,2626],{"__ignoreMap":43},[47,2590,2591],{"class":49,"line":50},[47,2592,2593],{"class":86},"\u002F\u002F Плохо: nil вызовет панику внутри при обращении к ctx.Done()\n",[47,2595,2596,2599,2601,2603],{"class":49,"line":68},[47,2597,2598],{"class":57},"processOrder",[47,2600,140],{"class":64},[47,2602,1260],{"class":234},[47,2604,2605],{"class":64},", orderID)\n",[47,2607,2608],{"class":49,"line":90},[47,2609,308],{"emptyLinePlaceholder":307},[47,2611,2612],{"class":49,"line":104},[47,2613,2614],{"class":86},"\u002F\u002F Хорошо: если нет контекста — используй Background или TODO\n",[47,2616,2617,2619,2621,2623],{"class":49,"line":134},[47,2618,2598],{"class":57},[47,2620,554],{"class":64},[47,2622,296],{"class":57},[47,2624,2625],{"class":64},"(), orderID)\n",[47,2627,2628,2630,2632,2634,2637],{"class":49,"line":158},[47,2629,2598],{"class":57},[47,2631,554],{"class":64},[47,2633,444],{"class":57},[47,2635,2636],{"class":64},"(), orderID) ",[47,2638,2639],{"class":86},"\u002F\u002F если контекст появится позже\n",[686,2641,2643],{"id":2642},"игнорирование-cancel","Игнорирование cancel",[38,2645,2647],{"className":40,"code":2646,"language":42,"meta":43,"style":43},"\u002F\u002F Утечка: cancel никогда не вызывается\nctx, _ := context.WithTimeout(parent, time.Second)\n\n\u002F\u002F Правильно\nctx, cancel := context.WithTimeout(parent, time.Second)\ndefer cancel()\n",[19,2648,2649,2654,2668,2672,2677,2689],{"__ignoreMap":43},[47,2650,2651],{"class":49,"line":50},[47,2652,2653],{"class":86},"\u002F\u002F Утечка: cancel никогда не вызывается\n",[47,2655,2656,2659,2661,2663,2665],{"class":49,"line":68},[47,2657,2658],{"class":64},"ctx, _ ",[47,2660,214],{"class":53},[47,2662,293],{"class":64},[47,2664,1052],{"class":57},[47,2666,2667],{"class":64},"(parent, time.Second)\n",[47,2669,2670],{"class":49,"line":90},[47,2671,308],{"emptyLinePlaceholder":307},[47,2673,2674],{"class":49,"line":104},[47,2675,2676],{"class":86},"\u002F\u002F Правильно\n",[47,2678,2679,2681,2683,2685,2687],{"class":49,"line":134},[47,2680,1045],{"class":64},[47,2682,214],{"class":53},[47,2684,293],{"class":64},[47,2686,1052],{"class":57},[47,2688,2667],{"class":64},[47,2690,2691,2693,2695],{"class":49,"line":158},[47,2692,1069],{"class":53},[47,2694,567],{"class":57},[47,2696,419],{"class":64},[24,2698],{},[27,2700,2702],{"id":2701},"вопросы-на-собеседовании","Вопросы на собеседовании",[15,2704,2705,2708,2711],{},[2099,2706,2707],{},"Q: Что такое Context и зачем он нужен?",[2709,2710],"br",{},"\nA: Механизм для передачи сигнала отмены, дедлайнов и request-scoped данных через цепочку вызовов. Решает задачу: как остановить все горутины запроса если клиент ушёл или истёк таймаут. Контекст передаётся явно первым аргументом — это намеренное решение, делающее поддержку отмены видимой в API.",[15,2713,2714,2717,2719,2720,2722,2723,2726,2727,2729,2730,2732,2733,2736,2737,116],{},[2099,2715,2716],{},"Q: Чем отличаются WithCancel, WithTimeout и WithDeadline?",[2709,2718],{},"\nA: ",[19,2721,320],{}," — отмена только явным вызовом ",[19,2724,2725],{},"cancel()",". ",[19,2728,1052],{}," — автоматическая отмена через заданную продолжительность. ",[19,2731,1133],{}," — автоматическая отмена в конкретный момент времени. ",[19,2734,2735],{},"WithTimeout(ctx, d)"," — синтаксический сахар над ",[19,2738,2739],{},"WithDeadline(ctx, time.Now().Add(d))",[15,2741,2742,2745,2747,2748,2750],{},[2099,2743,2744],{},"Q: Что произойдёт если не вызвать cancel()?",[2709,2746],{},"\nA: Утечка ресурсов: контекст и все его дочерние контексты не будут освобождены до отмены родителя. В долгоживущих сервисах это накапливается. Поэтому ",[19,2749,680],{}," — обязательное правило сразу после создания контекста.",[15,2752,2753,2756,2758],{},[2099,2754,2755],{},"Q: Как устроено дерево контекстов? Что происходит при отмене родителя?",[2709,2757],{},"\nA: Контексты образуют дерево — каждый дочерний наследует свойства родителя. Отмена родителя автоматически рекурсивно отменяет всех потомков. Дочерний может отмениться раньше родителя, но не позже.",[15,2760,2761,2764,2766,2767,2770],{},[2099,2762,2763],{},"Q: Почему нельзя использовать строку как ключ для WithValue?",[2709,2765],{},"\nA: Коллизии между пакетами: разные пакеты могут использовать одинаковые строки и перезаписать значения друг друга. Собственный неэкспортируемый тип ключа (",[19,2768,2769],{},"type myKey struct{}",") гарантирует уникальность — разные пакеты не могут создать одинаковый тип.",[15,2772,2773,2776,2778],{},[2099,2774,2775],{},"Q: Что передавать через контекст, а что нет?",[2709,2777],{},"\nA: Через контекст — cross-cutting concerns запроса: request ID, trace ID, user ID аутентифицированного пользователя, tenant ID. Не через контекст: параметры бизнес-логики (явные аргументы), зависимости (инъекция через конструктор), ошибки (явный возврат). Контекст не замена аргументам функции.",[15,2780,2781,2784,2786],{},[2099,2782,2783],{},"Q: Почему нельзя хранить контекст в структуре?",[2709,2785],{},"\nA: Контекст привязан к конкретному запросу и имеет время жизни запроса. Структура может жить дольше — хранение контекста в ней означает риск использования устаревшего или уже отменённого контекста. Контекст всегда передаётся через аргумент метода.",[15,2788,2789,2792,2794,2795,2798,2799,2802,2803,2805,2806,2809,2810,116],{},[2099,2790,2791],{},"Q: Как проверить причину отмены контекста?",[2709,2793],{},"\nA: Через ",[19,2796,2797],{},"ctx.Err()",": возвращает ",[19,2800,2801],{},"context.Canceled"," при явной отмене через ",[19,2804,2725],{},", и ",[19,2807,2808],{},"context.DeadlineExceeded"," при истечении таймаута или дедлайна. Пока контекст активен — возвращает ",[19,2811,1260],{},[15,2813,2814,2817,2819],{},[2099,2815,2816],{},"Q: Может ли дочерний контекст расширить дедлайн родителя?",[2709,2818],{},"\nA: Нет. Если родитель отменится раньше — дочерний тоже отменится, даже если его собственный дедлайн ещё не наступил. Дочерний контекст может только сузить дедлайн, но не расширить.",[11,2821,2823],{"id":2822},"задачи-context","Задачи: Context",[24,2825],{},[15,2827,2828],{},[2099,2829,2830],{},"Задача 1: Передача request ID",[15,2832,2833,2836],{},[2099,2834,2835],{},"Уровень:"," Лёгкая",[15,2838,2839,2842],{},[2099,2840,2841],{},"Что проверяет:"," WithValue, правильный тип ключа, извлечение значений",[15,2844,2845,2848,2849,2852,2853,2856,2857,116],{},[2099,2846,2847],{},"Условие:"," Реализуй middleware-функцию ",[19,2850,2851],{},"withRequestID(ctx context.Context, id string) context.Context"," и функцию ",[19,2854,2855],{},"getRequestID(ctx context.Context) string",". Используй собственный тип ключа. Если ID нет в контексте — возвращай ",[19,2858,2859],{},"\"unknown\"",[15,2861,2862],{},[2099,2863,2864],{},"Решение:",[38,2866,2868],{"className":40,"code":2867,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\ntype contextKey string\n\nconst requestIDKey contextKey = \"requestID\"\n\nfunc withRequestID(ctx context.Context, id string) context.Context {\n    return context.WithValue(ctx, requestIDKey, id)\n}\n\nfunc getRequestID(ctx context.Context) string {\n    id, ok := ctx.Value(requestIDKey).(string)\n    if !ok || id == \"\" {\n        return \"unknown\"\n    }\n    return id\n}\n\nfunc handle(ctx context.Context) {\n    fmt.Printf(\"[%s] handling request\\n\", getRequestID(ctx))\n}\n\nfunc main() {\n    ctx := context.Background()\n    handle(ctx) \u002F\u002F [unknown] handling request\n\n    ctx = withRequestID(ctx, \"abc-123\")\n    handle(ctx) \u002F\u002F [abc-123] handling request\n}\n",[19,2869,2870,2876,2880,2886,2894,2902,2906,2910,2919,2923,2936,2940,2973,2984,2988,2992,3015,3032,3055,3061,3065,3072,3076,3080,3099,3130,3134,3138,3146,3159,3171,3176,3191,3201],{"__ignoreMap":43},[47,2871,2872,2874],{"class":49,"line":50},[47,2873,1711],{"class":53},[47,2875,1714],{"class":57},[47,2877,2878],{"class":49,"line":68},[47,2879,308],{"emptyLinePlaceholder":307},[47,2881,2882,2884],{"class":49,"line":90},[47,2883,1723],{"class":53},[47,2885,1726],{"class":64},[47,2887,2888,2890,2892],{"class":49,"line":104},[47,2889,1731],{"class":1342},[47,2891,1734],{"class":57},[47,2893,1737],{"class":1342},[47,2895,2896,2898,2900],{"class":49,"line":134},[47,2897,1731],{"class":1342},[47,2899,1744],{"class":57},[47,2901,1737],{"class":1342},[47,2903,2904],{"class":49,"line":158},[47,2905,1263],{"class":64},[47,2907,2908],{"class":49,"line":264},[47,2909,308],{"emptyLinePlaceholder":307},[47,2911,2912,2914,2916],{"class":49,"line":376},[47,2913,54],{"class":53},[47,2915,1761],{"class":57},[47,2917,2918],{"class":53}," string\n",[47,2920,2921],{"class":49,"line":575},[47,2922,308],{"emptyLinePlaceholder":307},[47,2924,2925,2927,2930,2932,2934],{"class":49,"line":580},[47,2926,1775],{"class":53},[47,2928,2929],{"class":234}," requestIDKey",[47,2931,1761],{"class":57},[47,2933,1787],{"class":53},[47,2935,1790],{"class":1342},[47,2937,2938],{"class":49,"line":591},[47,2939,308],{"emptyLinePlaceholder":307},[47,2941,2942,2944,2947,2949,2951,2953,2955,2957,2959,2961,2963,2965,2967,2969,2971],{"class":49,"line":597},[47,2943,178],{"class":53},[47,2945,2946],{"class":57}," withRequestID",[47,2948,140],{"class":64},[47,2950,167],{"class":143},[47,2952,188],{"class":57},[47,2954,116],{"class":64},[47,2956,35],{"class":57},[47,2958,122],{"class":64},[47,2960,864],{"class":143},[47,2962,1190],{"class":53},[47,2964,128],{"class":64},[47,2966,1734],{"class":57},[47,2968,116],{"class":64},[47,2970,35],{"class":57},[47,2972,65],{"class":64},[47,2974,2975,2977,2979,2981],{"class":49,"line":608},[47,2976,255],{"class":53},[47,2978,293],{"class":64},[47,2980,1696],{"class":57},[47,2982,2983],{"class":64},"(ctx, requestIDKey, id)\n",[47,2985,2986],{"class":49,"line":616},[47,2987,161],{"class":64},[47,2989,2990],{"class":49,"line":622},[47,2991,308],{"emptyLinePlaceholder":307},[47,2993,2994,2996,2999,3001,3003,3005,3007,3009,3011,3013],{"class":49,"line":627},[47,2995,178],{"class":53},[47,2997,2998],{"class":57}," getRequestID",[47,3000,140],{"class":64},[47,3002,167],{"class":143},[47,3004,188],{"class":57},[47,3006,116],{"class":64},[47,3008,35],{"class":57},[47,3010,128],{"class":64},[47,3012,1848],{"class":53},[47,3014,65],{"class":64},[47,3016,3017,3020,3022,3024,3026,3028,3030],{"class":49,"line":640},[47,3018,3019],{"class":64},"    id, ok ",[47,3021,214],{"class":53},[47,3023,755],{"class":64},[47,3025,1842],{"class":57},[47,3027,1845],{"class":64},[47,3029,1848],{"class":53},[47,3031,1263],{"class":64},[47,3033,3034,3036,3038,3041,3044,3047,3050,3053],{"class":49,"line":653},[47,3035,225],{"class":53},[47,3037,1857],{"class":53},[47,3039,3040],{"class":64},"ok ",[47,3042,3043],{"class":53},"||",[47,3045,3046],{"class":64}," id ",[47,3048,3049],{"class":53},"==",[47,3051,3052],{"class":1342}," \"\"",[47,3054,65],{"class":64},[47,3056,3057,3059],{"class":49,"line":668},[47,3058,242],{"class":53},[47,3060,1870],{"class":1342},[47,3062,3063],{"class":49,"line":673},[47,3064,250],{"class":64},[47,3066,3067,3069],{"class":49,"line":1892},[47,3068,255],{"class":53},[47,3070,3071],{"class":64}," id\n",[47,3073,3074],{"class":49,"line":1897},[47,3075,161],{"class":64},[47,3077,3078],{"class":49,"line":1902},[47,3079,308],{"emptyLinePlaceholder":307},[47,3081,3082,3084,3087,3089,3091,3093,3095,3097],{"class":49,"line":1912},[47,3083,178],{"class":53},[47,3085,3086],{"class":57}," handle",[47,3088,140],{"class":64},[47,3090,167],{"class":143},[47,3092,188],{"class":57},[47,3094,116],{"class":64},[47,3096,35],{"class":57},[47,3098,1205],{"class":64},[47,3100,3101,3103,3106,3108,3111,3114,3117,3120,3122,3124,3127],{"class":49,"line":1926},[47,3102,1879],{"class":64},[47,3104,3105],{"class":57},"Printf",[47,3107,140],{"class":64},[47,3109,3110],{"class":1342},"\"[",[47,3112,3113],{"class":234},"%s",[47,3115,3116],{"class":1342},"] handling request",[47,3118,3119],{"class":234},"\\n",[47,3121,1349],{"class":1342},[47,3123,122],{"class":64},[47,3125,3126],{"class":57},"getRequestID",[47,3128,3129],{"class":64},"(ctx))\n",[47,3131,3132],{"class":49,"line":1946},[47,3133,161],{"class":64},[47,3135,3136],{"class":49,"line":1960},[47,3137,308],{"emptyLinePlaceholder":307},[47,3139,3140,3142,3144],{"class":49,"line":1968},[47,3141,178],{"class":53},[47,3143,1907],{"class":57},[47,3145,540],{"class":64},[47,3147,3149,3151,3153,3155,3157],{"class":49,"line":3148},29,[47,3150,1929],{"class":64},[47,3152,214],{"class":53},[47,3154,293],{"class":64},[47,3156,296],{"class":57},[47,3158,419],{"class":64},[47,3160,3162,3165,3168],{"class":49,"line":3161},30,[47,3163,3164],{"class":57},"    handle",[47,3166,3167],{"class":64},"(ctx) ",[47,3169,3170],{"class":86},"\u002F\u002F [unknown] handling request\n",[47,3172,3174],{"class":49,"line":3173},31,[47,3175,308],{"emptyLinePlaceholder":307},[47,3177,3179,3181,3183,3185,3187,3189],{"class":49,"line":3178},32,[47,3180,1929],{"class":64},[47,3182,1081],{"class":53},[47,3184,2946],{"class":57},[47,3186,2000],{"class":64},[47,3188,1941],{"class":1342},[47,3190,1263],{"class":64},[47,3192,3194,3196,3198],{"class":49,"line":3193},33,[47,3195,3164],{"class":57},[47,3197,3167],{"class":64},[47,3199,3200],{"class":86},"\u002F\u002F [abc-123] handling request\n",[47,3202,3204],{"class":49,"line":3203},34,[47,3205,161],{"class":64},[24,3207],{},[15,3209,3210],{},[2099,3211,3212],{},"Задача 2: Отмена по первому результату",[15,3214,3215,3217],{},[2099,3216,2835],{}," Средняя",[15,3219,3220,3222],{},[2099,3221,2841],{}," WithCancel, паттерн first-result-wins",[15,3224,3225,3227,3228,3231],{},[2099,3226,2847],{}," Напиши функцию ",[19,3229,3230],{},"fastest(ctx context.Context, fns []func(ctx context.Context) int) int"," которая запускает все функции конкурентно и возвращает результат той которая завершится первой. Остальные горутины должны получить сигнал отмены.",[15,3233,3234],{},[2099,3235,2864],{},[38,3237,3239],{"className":40,"code":3238,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"time\"\n)\n\nfunc fastest(ctx context.Context, fns []func(ctx context.Context) int) int {\n    ctx, cancel := context.WithCancel(ctx)\n    defer cancel()\n\n    ch := make(chan int, len(fns)) \u002F\u002F буферизованный — горутины не зависнут\n\n    for _, fn := range fns {\n        fn := fn\n        go func() {\n            result := fn(ctx)\n            select {\n            case ch \u003C- result:\n            case \u003C-ctx.Done():\n            }\n        }()\n    }\n\n    result := \u003C-ch\n    cancel() \u002F\u002F отменяем остальные\n    return result\n}\n\nfunc main() {\n    ctx := context.Background()\n\n    fns := []func(ctx context.Context) int{\n        func(ctx context.Context) int {\n            time.Sleep(300 * time.Millisecond)\n            return 1\n        },\n        func(ctx context.Context) int {\n            time.Sleep(100 * time.Millisecond)\n            return 2 \u002F\u002F победитель\n        },\n        func(ctx context.Context) int {\n            time.Sleep(200 * time.Millisecond)\n            return 3\n        },\n    }\n\n    fmt.Println(fastest(ctx, fns)) \u002F\u002F 2\n}\n",[19,3240,3241,3247,3251,3257,3265,3273,3281,3285,3289,3337,3349,3357,3361,3389,3393,3408,3418,3427,3439,3446,3460,3472,3476,3481,3485,3489,3501,3511,3518,3522,3526,3534,3546,3550,3578,3600,3617,3625,3631,3652,3667,3678,3683,3704,3720,3728,3733,3738,3743,3761],{"__ignoreMap":43},[47,3242,3243,3245],{"class":49,"line":50},[47,3244,1711],{"class":53},[47,3246,1714],{"class":57},[47,3248,3249],{"class":49,"line":68},[47,3250,308],{"emptyLinePlaceholder":307},[47,3252,3253,3255],{"class":49,"line":90},[47,3254,1723],{"class":53},[47,3256,1726],{"class":64},[47,3258,3259,3261,3263],{"class":49,"line":104},[47,3260,1731],{"class":1342},[47,3262,1734],{"class":57},[47,3264,1737],{"class":1342},[47,3266,3267,3269,3271],{"class":49,"line":134},[47,3268,1731],{"class":1342},[47,3270,1744],{"class":57},[47,3272,1737],{"class":1342},[47,3274,3275,3277,3279],{"class":49,"line":158},[47,3276,1731],{"class":1342},[47,3278,113],{"class":57},[47,3280,1737],{"class":1342},[47,3282,3283],{"class":49,"line":264},[47,3284,1263],{"class":64},[47,3286,3287],{"class":49,"line":376},[47,3288,308],{"emptyLinePlaceholder":307},[47,3290,3291,3293,3296,3298,3300,3302,3304,3306,3308,3311,3314,3316,3318,3320,3322,3324,3326,3328,3331,3333,3335],{"class":49,"line":575},[47,3292,178],{"class":53},[47,3294,3295],{"class":57}," fastest",[47,3297,140],{"class":64},[47,3299,167],{"class":143},[47,3301,188],{"class":57},[47,3303,116],{"class":64},[47,3305,35],{"class":57},[47,3307,122],{"class":64},[47,3309,3310],{"class":143},"fns",[47,3312,3313],{"class":64}," []",[47,3315,178],{"class":53},[47,3317,140],{"class":64},[47,3319,167],{"class":143},[47,3321,188],{"class":57},[47,3323,116],{"class":64},[47,3325,35],{"class":57},[47,3327,128],{"class":64},[47,3329,3330],{"class":53},"int",[47,3332,128],{"class":64},[47,3334,3330],{"class":53},[47,3336,65],{"class":64},[47,3338,3339,3341,3343,3345,3347],{"class":49,"line":580},[47,3340,545],{"class":64},[47,3342,214],{"class":53},[47,3344,293],{"class":64},[47,3346,320],{"class":57},[47,3348,637],{"class":64},[47,3350,3351,3353,3355],{"class":49,"line":591},[47,3352,564],{"class":53},[47,3354,567],{"class":57},[47,3356,419],{"class":64},[47,3358,3359],{"class":49,"line":597},[47,3360,308],{"emptyLinePlaceholder":307},[47,3362,3363,3366,3368,3371,3373,3376,3378,3380,3383,3386],{"class":49,"line":608},[47,3364,3365],{"class":64},"    ch ",[47,3367,214],{"class":53},[47,3369,3370],{"class":57}," make",[47,3372,140],{"class":64},[47,3374,3375],{"class":53},"chan",[47,3377,200],{"class":53},[47,3379,122],{"class":64},[47,3381,3382],{"class":57},"len",[47,3384,3385],{"class":64},"(fns)) ",[47,3387,3388],{"class":86},"\u002F\u002F буферизованный — горутины не зависнут\n",[47,3390,3391],{"class":49,"line":616},[47,3392,308],{"emptyLinePlaceholder":307},[47,3394,3395,3397,3400,3402,3405],{"class":49,"line":622},[47,3396,721],{"class":53},[47,3398,3399],{"class":64}," _, fn ",[47,3401,214],{"class":53},[47,3403,3404],{"class":53}," range",[47,3406,3407],{"class":64}," fns {\n",[47,3409,3410,3413,3415],{"class":49,"line":627},[47,3411,3412],{"class":64},"        fn ",[47,3414,214],{"class":53},[47,3416,3417],{"class":64}," fn\n",[47,3419,3420,3423,3425],{"class":49,"line":640},[47,3421,3422],{"class":53},"        go",[47,3424,586],{"class":53},[47,3426,540],{"class":64},[47,3428,3429,3432,3434,3437],{"class":49,"line":653},[47,3430,3431],{"class":64},"            result ",[47,3433,214],{"class":53},[47,3435,3436],{"class":57}," fn",[47,3438,637],{"class":64},[47,3440,3441,3444],{"class":49,"line":668},[47,3442,3443],{"class":53},"            select",[47,3445,65],{"class":64},[47,3447,3448,3451,3454,3457],{"class":49,"line":673},[47,3449,3450],{"class":53},"            case",[47,3452,3453],{"class":64}," ch ",[47,3455,3456],{"class":53},"\u003C-",[47,3458,3459],{"class":64}," result:\n",[47,3461,3462,3464,3466,3468,3470],{"class":49,"line":1892},[47,3463,3450],{"class":53},[47,3465,738],{"class":53},[47,3467,741],{"class":64},[47,3469,744],{"class":57},[47,3471,747],{"class":64},[47,3473,3474],{"class":49,"line":1897},[47,3475,809],{"class":64},[47,3477,3478],{"class":49,"line":1902},[47,3479,3480],{"class":64},"        }()\n",[47,3482,3483],{"class":49,"line":1912},[47,3484,250],{"class":64},[47,3486,3487],{"class":49,"line":1926},[47,3488,308],{"emptyLinePlaceholder":307},[47,3490,3491,3494,3496,3498],{"class":49,"line":1946},[47,3492,3493],{"class":64},"    result ",[47,3495,214],{"class":53},[47,3497,738],{"class":53},[47,3499,3500],{"class":64},"ch\n",[47,3502,3503,3506,3508],{"class":49,"line":1960},[47,3504,3505],{"class":57},"    cancel",[47,3507,74],{"class":64},[47,3509,3510],{"class":86},"\u002F\u002F отменяем остальные\n",[47,3512,3513,3515],{"class":49,"line":1968},[47,3514,255],{"class":53},[47,3516,3517],{"class":64}," result\n",[47,3519,3520],{"class":49,"line":3148},[47,3521,161],{"class":64},[47,3523,3524],{"class":49,"line":3161},[47,3525,308],{"emptyLinePlaceholder":307},[47,3527,3528,3530,3532],{"class":49,"line":3173},[47,3529,178],{"class":53},[47,3531,1907],{"class":57},[47,3533,540],{"class":64},[47,3535,3536,3538,3540,3542,3544],{"class":49,"line":3178},[47,3537,1929],{"class":64},[47,3539,214],{"class":53},[47,3541,293],{"class":64},[47,3543,296],{"class":57},[47,3545,419],{"class":64},[47,3547,3548],{"class":49,"line":3193},[47,3549,308],{"emptyLinePlaceholder":307},[47,3551,3552,3555,3557,3559,3561,3563,3565,3567,3569,3571,3573,3575],{"class":49,"line":3203},[47,3553,3554],{"class":64},"    fns ",[47,3556,214],{"class":53},[47,3558,3313],{"class":64},[47,3560,178],{"class":53},[47,3562,140],{"class":64},[47,3564,167],{"class":143},[47,3566,188],{"class":57},[47,3568,116],{"class":64},[47,3570,35],{"class":57},[47,3572,128],{"class":64},[47,3574,3330],{"class":53},[47,3576,3577],{"class":64},"{\n",[47,3579,3581,3584,3586,3588,3590,3592,3594,3596,3598],{"class":49,"line":3580},35,[47,3582,3583],{"class":53},"        func",[47,3585,140],{"class":64},[47,3587,167],{"class":143},[47,3589,188],{"class":57},[47,3591,116],{"class":64},[47,3593,35],{"class":57},[47,3595,128],{"class":64},[47,3597,3330],{"class":53},[47,3599,65],{"class":64},[47,3601,3603,3606,3608,3610,3613,3615],{"class":49,"line":3602},36,[47,3604,3605],{"class":64},"            time.",[47,3607,504],{"class":57},[47,3609,140],{"class":64},[47,3611,3612],{"class":234},"300",[47,3614,512],{"class":53},[47,3616,515],{"class":64},[47,3618,3620,3622],{"class":49,"line":3619},37,[47,3621,752],{"class":53},[47,3623,3624],{"class":234}," 1\n",[47,3626,3628],{"class":49,"line":3627},38,[47,3629,3630],{"class":64},"        },\n",[47,3632,3634,3636,3638,3640,3642,3644,3646,3648,3650],{"class":49,"line":3633},39,[47,3635,3583],{"class":53},[47,3637,140],{"class":64},[47,3639,167],{"class":143},[47,3641,188],{"class":57},[47,3643,116],{"class":64},[47,3645,35],{"class":57},[47,3647,128],{"class":64},[47,3649,3330],{"class":53},[47,3651,65],{"class":64},[47,3653,3655,3657,3659,3661,3663,3665],{"class":49,"line":3654},40,[47,3656,3605],{"class":64},[47,3658,504],{"class":57},[47,3660,140],{"class":64},[47,3662,509],{"class":234},[47,3664,512],{"class":53},[47,3666,515],{"class":64},[47,3668,3670,3672,3675],{"class":49,"line":3669},41,[47,3671,752],{"class":53},[47,3673,3674],{"class":234}," 2",[47,3676,3677],{"class":86}," \u002F\u002F победитель\n",[47,3679,3681],{"class":49,"line":3680},42,[47,3682,3630],{"class":64},[47,3684,3686,3688,3690,3692,3694,3696,3698,3700,3702],{"class":49,"line":3685},43,[47,3687,3583],{"class":53},[47,3689,140],{"class":64},[47,3691,167],{"class":143},[47,3693,188],{"class":57},[47,3695,116],{"class":64},[47,3697,35],{"class":57},[47,3699,128],{"class":64},[47,3701,3330],{"class":53},[47,3703,65],{"class":64},[47,3705,3707,3709,3711,3713,3716,3718],{"class":49,"line":3706},44,[47,3708,3605],{"class":64},[47,3710,504],{"class":57},[47,3712,140],{"class":64},[47,3714,3715],{"class":234},"200",[47,3717,512],{"class":53},[47,3719,515],{"class":64},[47,3721,3723,3725],{"class":49,"line":3722},45,[47,3724,752],{"class":53},[47,3726,3727],{"class":234}," 3\n",[47,3729,3731],{"class":49,"line":3730},46,[47,3732,3630],{"class":64},[47,3734,3736],{"class":49,"line":3735},47,[47,3737,250],{"class":64},[47,3739,3741],{"class":49,"line":3740},48,[47,3742,308],{"emptyLinePlaceholder":307},[47,3744,3746,3748,3750,3752,3755,3758],{"class":49,"line":3745},49,[47,3747,1879],{"class":64},[47,3749,659],{"class":57},[47,3751,140],{"class":64},[47,3753,3754],{"class":57},"fastest",[47,3756,3757],{"class":64},"(ctx, fns)) ",[47,3759,3760],{"class":86},"\u002F\u002F 2\n",[47,3762,3764],{"class":49,"line":3763},50,[47,3765,161],{"class":64},[24,3767],{},[15,3769,3770],{},[2099,3771,3772],{},"Задача 3: Цепочка таймаутов",[15,3774,3775,3777],{},[2099,3776,2835],{}," Сложная",[15,3779,3780,3782],{},[2099,3781,2841],{}," наследование дедлайнов, понимание что дочерний не расширяет родительский",[15,3784,3785,3787],{},[2099,3786,2847],{}," Что выведет код? Объясни почему.",[38,3789,3791],{"className":40,"code":3790,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"time\"\n)\n\nfunc operation(ctx context.Context, name string, duration time.Duration) error {\n    select {\n    case \u003C-time.After(duration):\n        fmt.Printf(\"%s: completed\\n\", name)\n        return nil\n    case \u003C-ctx.Done():\n        fmt.Printf(\"%s: cancelled — %v\\n\", name, ctx.Err())\n        return ctx.Err()\n    }\n}\n\nfunc main() {\n    parent, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)\n    defer cancel()\n\n    child, cancelChild := context.WithTimeout(parent, 500*time.Millisecond)\n    defer cancelChild()\n\n    operation(child, \"fast\", 100*time.Millisecond)\n    operation(child, \"slow\", 300*time.Millisecond)\n}\n",[19,3792,3793,3799,3803,3809,3817,3825,3833,3837,3841,3884,3891,3906,3928,3934,3946,3973,3983,3987,3991,3995,4003,4026,4034,4038,4057,4066,4070,4089,4106],{"__ignoreMap":43},[47,3794,3795,3797],{"class":49,"line":50},[47,3796,1711],{"class":53},[47,3798,1714],{"class":57},[47,3800,3801],{"class":49,"line":68},[47,3802,308],{"emptyLinePlaceholder":307},[47,3804,3805,3807],{"class":49,"line":90},[47,3806,1723],{"class":53},[47,3808,1726],{"class":64},[47,3810,3811,3813,3815],{"class":49,"line":104},[47,3812,1731],{"class":1342},[47,3814,1734],{"class":57},[47,3816,1737],{"class":1342},[47,3818,3819,3821,3823],{"class":49,"line":134},[47,3820,1731],{"class":1342},[47,3822,1744],{"class":57},[47,3824,1737],{"class":1342},[47,3826,3827,3829,3831],{"class":49,"line":158},[47,3828,1731],{"class":1342},[47,3830,113],{"class":57},[47,3832,1737],{"class":1342},[47,3834,3835],{"class":49,"line":264},[47,3836,1263],{"class":64},[47,3838,3839],{"class":49,"line":376},[47,3840,308],{"emptyLinePlaceholder":307},[47,3842,3843,3845,3848,3850,3852,3854,3856,3858,3860,3863,3865,3867,3870,3873,3875,3878,3880,3882],{"class":49,"line":575},[47,3844,178],{"class":53},[47,3846,3847],{"class":57}," operation",[47,3849,140],{"class":64},[47,3851,167],{"class":143},[47,3853,188],{"class":57},[47,3855,116],{"class":64},[47,3857,35],{"class":57},[47,3859,122],{"class":64},[47,3861,3862],{"class":143},"name",[47,3864,1190],{"class":53},[47,3866,122],{"class":64},[47,3868,3869],{"class":143},"duration",[47,3871,3872],{"class":57}," time",[47,3874,116],{"class":64},[47,3876,3877],{"class":57},"Duration",[47,3879,128],{"class":64},[47,3881,98],{"class":53},[47,3883,65],{"class":64},[47,3885,3886,3889],{"class":49,"line":580},[47,3887,3888],{"class":53},"    select",[47,3890,65],{"class":64},[47,3892,3893,3895,3897,3900,3903],{"class":49,"line":591},[47,3894,1511],{"class":53},[47,3896,738],{"class":53},[47,3898,3899],{"class":64},"time.",[47,3901,3902],{"class":57},"After",[47,3904,3905],{"class":64},"(duration):\n",[47,3907,3908,3910,3912,3914,3916,3918,3921,3923,3925],{"class":49,"line":597},[47,3909,656],{"class":64},[47,3911,3105],{"class":57},[47,3913,140],{"class":64},[47,3915,1349],{"class":1342},[47,3917,3113],{"class":234},[47,3919,3920],{"class":1342},": completed",[47,3922,3119],{"class":234},[47,3924,1349],{"class":1342},[47,3926,3927],{"class":64},", name)\n",[47,3929,3930,3932],{"class":49,"line":608},[47,3931,242],{"class":53},[47,3933,522],{"class":234},[47,3935,3936,3938,3940,3942,3944],{"class":49,"line":616},[47,3937,1511],{"class":53},[47,3939,738],{"class":53},[47,3941,741],{"class":64},[47,3943,744],{"class":57},[47,3945,747],{"class":64},[47,3947,3948,3950,3952,3954,3956,3958,3961,3964,3966,3969,3971],{"class":49,"line":622},[47,3949,656],{"class":64},[47,3951,3105],{"class":57},[47,3953,140],{"class":64},[47,3955,1349],{"class":1342},[47,3957,3113],{"class":234},[47,3959,3960],{"class":1342},": cancelled — ",[47,3962,3963],{"class":234},"%v\\n",[47,3965,1349],{"class":1342},[47,3967,3968],{"class":64},", name, ctx.",[47,3970,758],{"class":57},[47,3972,559],{"class":64},[47,3974,3975,3977,3979,3981],{"class":49,"line":627},[47,3976,242],{"class":53},[47,3978,755],{"class":64},[47,3980,758],{"class":57},[47,3982,419],{"class":64},[47,3984,3985],{"class":49,"line":640},[47,3986,250],{"class":64},[47,3988,3989],{"class":49,"line":653},[47,3990,161],{"class":64},[47,3992,3993],{"class":49,"line":668},[47,3994,308],{"emptyLinePlaceholder":307},[47,3996,3997,3999,4001],{"class":49,"line":673},[47,3998,178],{"class":53},[47,4000,1907],{"class":57},[47,4002,540],{"class":64},[47,4004,4005,4008,4010,4012,4014,4016,4018,4020,4022,4024],{"class":49,"line":1892},[47,4006,4007],{"class":64},"    parent, cancel ",[47,4009,214],{"class":53},[47,4011,293],{"class":64},[47,4013,1052],{"class":57},[47,4015,554],{"class":64},[47,4017,296],{"class":57},[47,4019,1222],{"class":64},[47,4021,3715],{"class":234},[47,4023,1061],{"class":53},[47,4025,1676],{"class":64},[47,4027,4028,4030,4032],{"class":49,"line":1897},[47,4029,564],{"class":53},[47,4031,567],{"class":57},[47,4033,419],{"class":64},[47,4035,4036],{"class":49,"line":1902},[47,4037,308],{"emptyLinePlaceholder":307},[47,4039,4040,4043,4045,4047,4049,4051,4053,4055],{"class":49,"line":1912},[47,4041,4042],{"class":64},"    child, cancelChild ",[47,4044,214],{"class":53},[47,4046,293],{"class":64},[47,4048,1052],{"class":57},[47,4050,1055],{"class":64},[47,4052,1671],{"class":234},[47,4054,1061],{"class":53},[47,4056,1676],{"class":64},[47,4058,4059,4061,4064],{"class":49,"line":1926},[47,4060,564],{"class":53},[47,4062,4063],{"class":57}," cancelChild",[47,4065,419],{"class":64},[47,4067,4068],{"class":49,"line":1946},[47,4069,308],{"emptyLinePlaceholder":307},[47,4071,4072,4075,4078,4081,4083,4085,4087],{"class":49,"line":1960},[47,4073,4074],{"class":57},"    operation",[47,4076,4077],{"class":64},"(child, ",[47,4079,4080],{"class":1342},"\"fast\"",[47,4082,122],{"class":64},[47,4084,509],{"class":234},[47,4086,1061],{"class":53},[47,4088,1676],{"class":64},[47,4090,4091,4093,4095,4098,4100,4102,4104],{"class":49,"line":1968},[47,4092,4074],{"class":57},[47,4094,4077],{"class":64},[47,4096,4097],{"class":1342},"\"slow\"",[47,4099,122],{"class":64},[47,4101,3612],{"class":234},[47,4103,1061],{"class":53},[47,4105,1676],{"class":64},[47,4107,4108],{"class":49,"line":3148},[47,4109,161],{"class":64},[15,4111,4112],{},[2099,4113,4114],{},"Ожидаемый ответ:",[38,4116,4121],{"className":4117,"code":4119,"language":4120},[4118],"language-text","fast: completed\nslow: cancelled — context deadline exceeded\n","text",[19,4122,4119],{"__ignoreMap":43},[15,4124,4125],{},[2099,4126,2864],{},[38,4128,4130],{"className":40,"code":4129,"language":42,"meta":43,"style":43},"\u002F\u002F parent: таймаут 200ms\n\u002F\u002F child: таймаут 500ms — но ограничен parent'ом в 200ms\n\n\u002F\u002F \"fast\" (100ms): завершается за 100ms — успевает до любого дедлайна.\n\u002F\u002F После \"fast\" прошло ~100ms.\n\n\u002F\u002F \"slow\" (300ms): нужно ещё 300ms, итого ~400ms.\n\u002F\u002F Но parent отменится через ~200ms от старта (100ms уже прошло, осталось ~100ms).\n\u002F\u002F child отменяется вместе с parent.\n\u002F\u002F Ошибка: context.DeadlineExceeded.\n\n\u002F\u002F Вывод: дочерний контекст НЕ может расширить дедлайн родителя.\n\u002F\u002F WithTimeout(parent, 500ms) реально даёт min(200ms, 500ms) = 200ms.\n",[19,4131,4132,4137,4142,4146,4151,4156,4160,4165,4170,4175,4180,4184,4189],{"__ignoreMap":43},[47,4133,4134],{"class":49,"line":50},[47,4135,4136],{"class":86},"\u002F\u002F parent: таймаут 200ms\n",[47,4138,4139],{"class":49,"line":68},[47,4140,4141],{"class":86},"\u002F\u002F child: таймаут 500ms — но ограничен parent'ом в 200ms\n",[47,4143,4144],{"class":49,"line":90},[47,4145,308],{"emptyLinePlaceholder":307},[47,4147,4148],{"class":49,"line":104},[47,4149,4150],{"class":86},"\u002F\u002F \"fast\" (100ms): завершается за 100ms — успевает до любого дедлайна.\n",[47,4152,4153],{"class":49,"line":134},[47,4154,4155],{"class":86},"\u002F\u002F После \"fast\" прошло ~100ms.\n",[47,4157,4158],{"class":49,"line":158},[47,4159,308],{"emptyLinePlaceholder":307},[47,4161,4162],{"class":49,"line":264},[47,4163,4164],{"class":86},"\u002F\u002F \"slow\" (300ms): нужно ещё 300ms, итого ~400ms.\n",[47,4166,4167],{"class":49,"line":376},[47,4168,4169],{"class":86},"\u002F\u002F Но parent отменится через ~200ms от старта (100ms уже прошло, осталось ~100ms).\n",[47,4171,4172],{"class":49,"line":575},[47,4173,4174],{"class":86},"\u002F\u002F child отменяется вместе с parent.\n",[47,4176,4177],{"class":49,"line":580},[47,4178,4179],{"class":86},"\u002F\u002F Ошибка: context.DeadlineExceeded.\n",[47,4181,4182],{"class":49,"line":591},[47,4183,308],{"emptyLinePlaceholder":307},[47,4185,4186],{"class":49,"line":597},[47,4187,4188],{"class":86},"\u002F\u002F Вывод: дочерний контекст НЕ может расширить дедлайн родителя.\n",[47,4190,4191],{"class":49,"line":608},[47,4192,4193],{"class":86},"\u002F\u002F WithTimeout(parent, 500ms) реально даёт min(200ms, 500ms) = 200ms.\n",[24,4195],{},[27,4197,4199],{"id":4198},"практика","Практика",[4201,4202,4204,4210,4244],"quiz",{"answer":1592,"id":4203,"xp":1634},"concurrency-context-q1",[15,4205,4206,4207,4209],{},"Какой вариант использования ",[19,4208,21],{}," считается нормальным для Go-кода?",[4211,4212,4213],"template",{"v-slot:options":43},[4214,4215,4216,4223,4229,4236],"ul",{},[4217,4218,4219,4220,4222],"li",{},"Хранить ",[19,4221,21],{}," в структуре сервиса и переиспользовать между запросами",[4217,4224,4225,4226,4228],{},"Передавать ",[19,4227,21],{}," первым аргументом функции, которая делает внешнюю работу",[4217,4230,4231,4232,4235],{},"Создавать один глобальный ",[19,4233,4234],{},"context.Background()"," на всё приложение и отменять его после каждого запроса",[4217,4237,4225,4238,4240,4241],{},[19,4239,21],{}," только когда в функции есть ",[19,4242,4243],{},"time.Sleep",[4211,4245,4246],{"v-slot:explanation":43},[15,4247,4248],{},"Контекст описывает конкретную операцию: запрос, команду, джобу, попытку обращения к внешнему сервису. Поэтому его обычно передают первым аргументом по цепочке вызовов. Хранить контекст в структуре опасно: легко смешать жизненные циклы разных операций.",[4250,4251,4255,4258,4398],"predict",{"answer":4252,"id":4253,"xp":4254},"context canceled","concurrency-context-p1","15",[15,4256,4257],{},"Что выведет программа?",[4211,4259,4260],{"v-slot:code":43},[38,4261,4263],{"className":40,"code":4262,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    cancel()\n\n    select {\n    case \u003C-ctx.Done():\n        fmt.Println(ctx.Err())\n    default:\n        fmt.Println(\"work\")\n    }\n}\n",[19,4264,4265,4271,4275,4281,4289,4297,4301,4305,4313,4329,4335,4339,4345,4357,4370,4377,4390,4394],{"__ignoreMap":43},[47,4266,4267,4269],{"class":49,"line":50},[47,4268,1711],{"class":53},[47,4270,1714],{"class":57},[47,4272,4273],{"class":49,"line":68},[47,4274,308],{"emptyLinePlaceholder":307},[47,4276,4277,4279],{"class":49,"line":90},[47,4278,1723],{"class":53},[47,4280,1726],{"class":64},[47,4282,4283,4285,4287],{"class":49,"line":104},[47,4284,1731],{"class":1342},[47,4286,1734],{"class":57},[47,4288,1737],{"class":1342},[47,4290,4291,4293,4295],{"class":49,"line":134},[47,4292,1731],{"class":1342},[47,4294,1744],{"class":57},[47,4296,1737],{"class":1342},[47,4298,4299],{"class":49,"line":158},[47,4300,1263],{"class":64},[47,4302,4303],{"class":49,"line":264},[47,4304,308],{"emptyLinePlaceholder":307},[47,4306,4307,4309,4311],{"class":49,"line":376},[47,4308,178],{"class":53},[47,4310,1907],{"class":57},[47,4312,540],{"class":64},[47,4314,4315,4317,4319,4321,4323,4325,4327],{"class":49,"line":575},[47,4316,545],{"class":64},[47,4318,214],{"class":53},[47,4320,293],{"class":64},[47,4322,320],{"class":57},[47,4324,554],{"class":64},[47,4326,296],{"class":57},[47,4328,559],{"class":64},[47,4330,4331,4333],{"class":49,"line":580},[47,4332,3505],{"class":57},[47,4334,419],{"class":64},[47,4336,4337],{"class":49,"line":591},[47,4338,308],{"emptyLinePlaceholder":307},[47,4340,4341,4343],{"class":49,"line":597},[47,4342,3888],{"class":53},[47,4344,65],{"class":64},[47,4346,4347,4349,4351,4353,4355],{"class":49,"line":608},[47,4348,1511],{"class":53},[47,4350,738],{"class":53},[47,4352,741],{"class":64},[47,4354,744],{"class":57},[47,4356,747],{"class":64},[47,4358,4359,4361,4363,4366,4368],{"class":49,"line":616},[47,4360,656],{"class":64},[47,4362,659],{"class":57},[47,4364,4365],{"class":64},"(ctx.",[47,4367,758],{"class":57},[47,4369,559],{"class":64},[47,4371,4372,4375],{"class":49,"line":622},[47,4373,4374],{"class":53},"    default",[47,4376,771],{"class":64},[47,4378,4379,4381,4383,4385,4388],{"class":49,"line":627},[47,4380,656],{"class":64},[47,4382,659],{"class":57},[47,4384,140],{"class":64},[47,4386,4387],{"class":1342},"\"work\"",[47,4389,1263],{"class":64},[47,4391,4392],{"class":49,"line":640},[47,4393,250],{"class":64},[47,4395,4396],{"class":49,"line":653},[47,4397,161],{"class":64},[4211,4399,4400],{"v-slot:hint":43},[15,4401,4402,4403,4405,4406,4408,4409,4411],{},"После вызова ",[19,4404,2725],{}," канал ",[19,4407,832],{}," закрыт. Закрытый канал читается немедленно, поэтому ветка ",[19,4410,836],{}," не выбирается.",[4413,4414,4418,4433,4594],"code-task",{"expected":4415,"id":4416,"xp":4417},"cancelled","concurrency-context-ct1","20",[15,4419,4420,4421,4424,4425,4428,4429,4432],{},"Реализуй ",[19,4422,4423],{},"waitForSignal",": функция должна вернуть сообщение из канала ",[19,4426,4427],{},"ch",", если оно пришло первым, или строку ",[19,4430,4431],{},"\"cancelled\"",", если контекст отменился раньше.",[4211,4434,4435],{"v-slot:template":43},[38,4436,4438],{"className":40,"code":4437,"language":42,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\nfunc waitForSignal(ctx context.Context, ch \u003C-chan string) string {\n    return \"\"\n}\n\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    cancel()\n\n    ch := make(chan string)\n    fmt.Println(waitForSignal(ctx, ch))\n}\n",[19,4439,4440,4446,4450,4456,4464,4472,4476,4480,4512,4519,4523,4527,4535,4551,4557,4561,4577,4590],{"__ignoreMap":43},[47,4441,4442,4444],{"class":49,"line":50},[47,4443,1711],{"class":53},[47,4445,1714],{"class":57},[47,4447,4448],{"class":49,"line":68},[47,4449,308],{"emptyLinePlaceholder":307},[47,4451,4452,4454],{"class":49,"line":90},[47,4453,1723],{"class":53},[47,4455,1726],{"class":64},[47,4457,4458,4460,4462],{"class":49,"line":104},[47,4459,1731],{"class":1342},[47,4461,1734],{"class":57},[47,4463,1737],{"class":1342},[47,4465,4466,4468,4470],{"class":49,"line":134},[47,4467,1731],{"class":1342},[47,4469,1744],{"class":57},[47,4471,1737],{"class":1342},[47,4473,4474],{"class":49,"line":158},[47,4475,1263],{"class":64},[47,4477,4478],{"class":49,"line":264},[47,4479,308],{"emptyLinePlaceholder":307},[47,4481,4482,4484,4487,4489,4491,4493,4495,4497,4499,4501,4504,4506,4508,4510],{"class":49,"line":376},[47,4483,178],{"class":53},[47,4485,4486],{"class":57}," waitForSignal",[47,4488,140],{"class":64},[47,4490,167],{"class":143},[47,4492,188],{"class":57},[47,4494,116],{"class":64},[47,4496,35],{"class":57},[47,4498,122],{"class":64},[47,4500,4427],{"class":143},[47,4502,4503],{"class":53}," \u003C-chan",[47,4505,1190],{"class":53},[47,4507,128],{"class":64},[47,4509,1848],{"class":53},[47,4511,65],{"class":64},[47,4513,4514,4516],{"class":49,"line":575},[47,4515,255],{"class":53},[47,4517,4518],{"class":1342}," \"\"\n",[47,4520,4521],{"class":49,"line":580},[47,4522,161],{"class":64},[47,4524,4525],{"class":49,"line":591},[47,4526,308],{"emptyLinePlaceholder":307},[47,4528,4529,4531,4533],{"class":49,"line":597},[47,4530,178],{"class":53},[47,4532,1907],{"class":57},[47,4534,540],{"class":64},[47,4536,4537,4539,4541,4543,4545,4547,4549],{"class":49,"line":608},[47,4538,545],{"class":64},[47,4540,214],{"class":53},[47,4542,293],{"class":64},[47,4544,320],{"class":57},[47,4546,554],{"class":64},[47,4548,296],{"class":57},[47,4550,559],{"class":64},[47,4552,4553,4555],{"class":49,"line":616},[47,4554,3505],{"class":57},[47,4556,419],{"class":64},[47,4558,4559],{"class":49,"line":622},[47,4560,308],{"emptyLinePlaceholder":307},[47,4562,4563,4565,4567,4569,4571,4573,4575],{"class":49,"line":627},[47,4564,3365],{"class":64},[47,4566,214],{"class":53},[47,4568,3370],{"class":57},[47,4570,140],{"class":64},[47,4572,3375],{"class":53},[47,4574,1190],{"class":53},[47,4576,1263],{"class":64},[47,4578,4579,4581,4583,4585,4587],{"class":49,"line":640},[47,4580,1879],{"class":64},[47,4582,659],{"class":57},[47,4584,140],{"class":64},[47,4586,4423],{"class":57},[47,4588,4589],{"class":64},"(ctx, ch))\n",[47,4591,4592],{"class":49,"line":653},[47,4593,161],{"class":64},[4211,4595,4596],{"v-slot:hints":43},[4214,4597,4598,4603,4611],{},[4217,4599,4600,4601],{},"Используй ",[19,4602,828],{},[4217,4604,4605,4606,4608,4609],{},"Чтение из ",[19,4607,832],{}," должно вернуть ",[19,4610,4431],{},[4217,4612,4605,4613,4615],{},[19,4614,4427],{}," должно вернуть полученную строку",[4617,4618,4619],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}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 pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":43,"searchDepth":68,"depth":68,"links":4621},[4622,4623,4624,4625,4628,4633,4637,4638,4643,4644],{"id":29,"depth":68,"text":30},{"id":274,"depth":68,"text":275},{"id":387,"depth":68,"text":388},{"id":460,"depth":68,"text":461,"children":4626},[4627],{"id":688,"depth":90,"text":689},{"id":1009,"depth":68,"text":1010,"children":4629},[4630,4631,4632],{"id":1166,"depth":90,"text":1167},{"id":1384,"depth":90,"text":1385},{"id":1558,"depth":90,"text":1559},{"id":1690,"depth":68,"text":1691,"children":4634},[4635,4636],{"id":1973,"depth":90,"text":1974},{"id":2093,"depth":90,"text":2094},{"id":2164,"depth":68,"text":2165},{"id":2414,"depth":68,"text":2415,"children":4639},[4640,4641,4642],{"id":2418,"depth":90,"text":2419},{"id":2582,"depth":90,"text":2583},{"id":2642,"depth":90,"text":2643},{"id":2701,"depth":68,"text":2702},{"id":4198,"depth":68,"text":4199},1781458310729]