2017-02-14 2 views
2

С событиями инициатор создает событие, которое будет получено теми процедурами, которые выбрали для получения этого события. Получатель определяет, какие события он получит от инициаторов.Понимание того, когда использовать события и когда использовать обратный вызов

С обратными вызовами процедура после завершения уведомляет вызывающего абонента о завершении.

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

Что должно быть хорошим подходом к использованию при кодировании, использовании событий или обратных вызовов?

ответ

2

События - для вещей, которые могут случиться много раз.

Обратные вызовы (или обещания) - для вещей, которые могут произойти один раз.

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

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

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

Во-первых, с обратных вызовов:

let fs = require('fs'); 
fs.readFile('a.txt', 'utf-8', (err, data) => { 
    if (err) { 
     console.log('Error:', err.message); 
    } else { 
     console.log('Data:', data.trim()); 
    } 
}); 

Если нет файла он будет печатать:

Error: ENOENT: no such file or directory, open 'a.txt' 

Если есть файл будет печатать:

Data: Contents of a.txt 

Теперь, то же с обещаниями:

let fs = require('mz/fs'); 
fs.readFile('b.txt', 'utf-8') 
    .then(data => { 
     console.log('Data:', data.trim()); 
    }) 
    .catch(err => { 
     console.log('Error:', err.message); 
    }); 

Он работает точно так же, как в предыдущем примере.

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

Например, это, с обратных вызовов:

let fs = require('fs'); 
function a(cb) { 
    fs.readFile('b.txt', 'utf-8', (err, data) => { 
     if (err) { 
      return cb('a() error: ' + err.message); 
     } 
     cb(null, 'a() data: ' + data.trim()); 
    }); 
} 
a((err, data) => { 
    if (err) { 
     console.log('Error:', err); 
    } else { 
     console.log('Data:', data); 
    } 
}); 

Он будет печатать либо это

Error: a() error: ENOENT: no such file or directory, open 'a.txt' 

или что-то вроде этого:

Data: a() data: Contents of a.txt 

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

let fs = require('mz/fs'); 
function a() { 
    return fs.readFile('a.txt', 'utf-8') 
     .then(data => 'a() data: ' + data.trim()) 
     .catch(err => Promise.reject('a() error: ' + err.message)); 
} 
let promise = a(); 
promise.then(data => console.log('Data:', data)) 
     .catch(err => console.log('Error:', err)); 

Он работает так же, как написано в другом стиле, что вы можете или не можете найти более удобными для чтения, но разница в том, что теперь вам не придется приложить обратный вызов в то время вызова функции a(). Вы можете сделать это в другом месте.

Если вы не хотите, чтобы изменить сообщение об ошибке, было бы это с обратными вызовами:

function a(cb) { 
    fs.readFile('a.txt', 'utf-8', (err, data) => { 
     if (err) { 
      return cb(err); 
     } 
     cb(null, 'a() data: ' + data.trim()); 
    }); 

и это с обещаниями:

function a() { 
    return fs.readFile('a.txt', 'utf-8') 
     .then(data => 'a() data: ' + data.trim()); 
} 

Другим отличием является то, что если у вас есть функция, которая возвращает обещание, вы можете использовать ключевое слово await внутри async function следующим образом:

async function x() { 
    try { 
     console.log('Data:', await a()); 
    } catch (err) { 
     console.log('Error:', err); 
    } 
} 

Вы не можете использовать await с функцией, которая не возвращает обещание.

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

Чтобы использовать async и await с узлом V7.x вам нужно использовать --harmony флаг, см:

+0

Можете ли вы помочь понять, когда я должен использовать обратный вызов, и когда я должен использовать обещание также? –

+1

@ Karan См. Мой обновленный ответ с гораздо большим количеством примеров с обратными вызовами и обещаниями. – rsp