Я решаю эту проблему с помощью стиля и соглашения о кодировании. И все время всплывает. Давайте возьмем ваш фрагмент ниже, немного уточнив его, чтобы у нас была работающая функция.
my_fn = (cb) ->
await socket.get 'image id', defer err, id
if err then return cb err, null
await Image.findById id, defer err, image
if err then return cb err, null
await check_permissions user, image, defer err, permitted
if err then return cb err, null
cb err, image
Вы совершенно правы, это уродливо, потому что во многих местах вы выходите из кода, и вам нужно не забывать вызывать cb каждый раз, когда вы возвращаетесь.
Другие приведенные вами фрагменты дают неверные результаты, поскольку они вводят параллелизм там, где требуется сериализация.
Мои личные соглашения по кодированию ICS: (1) возврат только один раз из функции (управление которой падает с конца); и (2) пытаться обрабатывать ошибки на одном уровне отступа. Переписывая то, что у вас есть, в моем предпочтительном стиле:
my_fn = (cb) ->
await socket.get 'image id', defer err, id
await Image.findById id, defer err, image unless err?
await check_permissions user, image, defer err, permitted unless err?
cb err, image
В случае ошибки в вызове socket.get вам нужно проверить ошибку дважды, и оба раза она, очевидно, не удастся. Я не думаю, что это конец света, так как это делает код чище.
В качестве альтернативы вы можете сделать это:
my_fn = (autocb) ->
await socket.get 'image id', defer err, id
if err then return [ err, null ]
await Image.findById id, defer err, image
if err then return [ err, null ]
await check_permissions user, image, defer err, permitted
return [ err, image ]
Если вы используете autocb, что не является моей любимой функцией ICS, то компилятор будет вызывать autocb каждый раз, когда вы возвращаетесь/замыкаетесь из функции. По опыту я считаю, что эта конструкция более подвержена ошибкам. Например, представьте, что вам нужно получить блокировку в начале функции, теперь вам нужно снять ее n раз. Другие могут не согласиться.
Еще одно замечание, указанное ниже в комментариях. autocb
работает как return
в том смысле, что принимает только одно значение. Если вы хотите вернуть несколько значений, как в этом примере, вам нужно вернуть массив или словарь. defer
выполняет задания по деструктуризации, чтобы помочь вам:
await my_fn defer [err, image]
person
Max Krohn
schedule
14.01.2013