2016-08-09 2 views
6

Общепринято для проверки аргументов и возврата ошибок в функциях.Обработка ошибок обратного вызова JavaScript

Однако в функции обратного вызова JavaScript, таких как:

function myFunction(num, callback) { 
    if (typeof num !== 'number') return callback(new Error('invalid num')) 
    // do something else asynchronously and callback(null, result) 
} 

Я написал много функций, как это, но мне интересно, если есть что-то потенциально вредно. Поскольку в большинстве случаев вызывающий абонент предполагает, что это асинхронная функция, а обратный вызов будет выполняться после кода сразу после вызова функции. Но если некоторые аргументы недействительны, функция немедленно вызовет обратный вызов. Таким образом, вызывающий абонент должен быть осторожным с ситуацией, то есть с неожиданной последовательностью выполнения.

Я хочу услышать некоторые советы по этой проблеме. Должен ли я тщательно предположить, что все асинхронные обратные вызовы могут быть выполнены немедленно? Или я должен использовать что-то вроде setTimeout (..., 0) для преобразования синхронной вещи в асинхронную. Или есть лучшее решение, о котором я не знаю. Благодарю.

ответ

3

АНИ должен документально подтвердить, что он будет вызывать обратный вызов либо синхронно (как Array#sort) или асинхронно (как Promise#then), а затем всегда подчиняются этой документированной гарантии. Он не должен смешивать-и-матч.

Так что да, если у вас есть функция, которая обычно вызывает обратный вызов асинхронно, она должна всегда вызывать ее асинхронно, независимо от того, почему она делает вызов.

Был отличный пример в JQuery: Когда JQuery первый добавил «отсроченные» объекты, они будут вызывать обратный вызов синхронно если отложенный уже решен, но асинхронно, если оно не было. Это стало источником множества путаницы и ошибок, что является частью того, почему обещания ES2015 гарантируют, что обратные вызовы then и catch всегда будут вызываться асинхронно.


Если это возможно, и не в ладах с остальной частью кодового, рассмотрят использование Promises, а не простые обратных вызовов. Обещания обеспечивают очень четкую, простую, гарантированную семантику и композиционную способность для асинхронных операций (и взаимодействия с синхронными).

+0

Я удалил свой нижний план. Я предполагаю, что валидация того, как называется функция, достаточно серьезна, чтобы бросить и не должна произойти в процессе производства. –

+0

@PatrickRoberts: Я удалил эту часть ответа, это не имело никакого отношения к вопросу. Я вижу вашу точку зрения, хотя я не согласен с ней; Мне нужно больше думать об этом. Я отмечаю, что если вы выбрали во время установки ES2015 Promise ('let p = new Promise (resolve => {throw new Error();})'), он преобразуется в отклонение конструктором Promise, который поддерживает ваш аргумент в пользу единственного канала ошибки, учитывая мысли и опыт, которые вошли в дизайн этого API ... –

+0

Мой подход также неверен. Как заметил мне комментатор, callstack - это что-то очень важное для рассмотрения, особенно когда разработчик, использующий вашу функцию, пытается повторно выполнить повторную попытку, в конечном итоге приводит к переходу stackoverflow, если ваша функция синхронна при ее ошибках. –

-1

Ну, так как вызывающий абонент ожидает, что функция будет асинхронной, выполняя функцию обратного вызова немедленно или через несколько секунд, не имеет значения.

Вам не нужно делать return, так как у вас есть функция обратного вызова.

+0

«Возврат» находится на месте, чтобы выйти из функции раньше, а не для обеспечения значения. –

+0

Извините, не видел там комментариев. –

0

Нет, вызов сразу же не вреден, а на самом деле намеренно задерживает ошибку, просто теряя время и накладные расходы. Да, вызов сразу для ошибки может быть очень вредным и должен быть исключен для функции, которая считается асинхронной! (Посмотрите на это, 180!)

С точки зрения разработчика, есть несколько веских причин, почему настройка может быть выполнена только после. Например here:

const server = net.createServer(() => {}).listen(8080); 

server.on('listening',() => {}); 

listening событие не прилагается, пока .listen(8080) вызывается, так как источник события от вызова к .listen().В этом случае выполнение события listening для синхронного вызова после выполнения .listen() будет безуспешным.

Вот еще один случай, который я хотел бы представить:

var num = '5'; 

myFunction(num, function callback(err, result) { 
    if (err) { 
    return myFunction(num, callback); 
    } 

    // handle result 
}); 

Теперь, если вы callback с ошибкой синхронно, этот поток управления приведет к StackOverflow. Хотя это ошибка разработчика, переполнение stackoverflow - это очень плохое из-за функции, которая, как ожидается, будет асинхронной. Это одно из преимуществ использования setImmediate(), чтобы передать ошибку, а не немедленно выполнить команду callback.

0

Вызывающая ваша асинхронная функция должна знать, что будет результатом вызова функции. Существует стандарт, для которого должна возвращаться асинхронная функция, Promises.

Если ваша функция возвращает Promise, любой может легко понять, что происходит в этой функции. Обещания имеют обратный вызов отклонения, но мы можем утверждать, следует ли проверять параметры параметров, отвергая обещание или если исключение должно быть выбрано прямо. В любом случае, если вызывающий обработчик правильно обрабатывает исключения с использованием метода catch, как непосредственно полученные исключения, так и отклонения будут записаны таким же образом.

function throwingFunction(num) { 
    return new Promise(function (resolve, reject) { 

    if (typeof num !== 'number') throw new Error('invalid num'); 
    // do something else asynchronously and callback(null, result) 
    }; 
} 

function rejectingFunction(num) { 
    return new Promise(function (resolve, reject) { 

    if (typeof num !== 'number') reject(new Error('invalid num')); 
    // do something else asynchronously and callback(null, result) 
    }; 
} 

// Instead of passing the callback, create the promise and provide your callback to the `then` method. 

var resultThrowing = throwingFunction(num) 
    .then(function (result) { console.log(result); }) 
    .catch(function (error) { console.log(error); }); 

var resultRejecting = rejectingFunction(num) 
    .then(function (result) { console.log(result); }) 
    .catch(function (error) { console.log(error); }); 

Оба шаблона приведут к тому, что ошибка будет обнаружена и зарегистрирована.

Если вы используете обещания, то вызывающий абонент вашей асинхронной функции не должен будет беспокоиться о вашей реализации внутри функции, и вы можете либо выбросить ошибку прямо в сторону, либо отклонить обещание по вашему усмотрению.

Смежные вопросы