Javascript 了解何时使用事件以及何时使用回调

Javascript 了解何时使用事件以及何时使用回调,javascript,node.js,Javascript,Node.js,对于事件,启动器引发一个事件,该事件将由选择接收该事件的例程接收。接收器指定它将从哪些启动器接收哪些事件 对于回调,完成后的例程通知调用方完成 因此,我很困惑应该在哪里使用事件,或者应该在哪里使用回调,因为我可以完成回调对事件的处理,但会导致在应用程序中创建大量事件 编写代码时,使用事件或回调应该遵循什么好方法?事件-用于可能发生多次的事件 回访(或承诺)-对于可能发生一次的事情 例如,当您调用一个函数,因为您需要从某个API获取当前温度时,该函数应该返回一个承诺或获取一个回调,该回调稍后可以使

对于事件,启动器引发一个事件,该事件将由选择接收该事件的例程接收。接收器指定它将从哪些启动器接收哪些事件

对于回调,完成后的例程通知调用方完成

因此,我很困惑应该在哪里使用事件,或者应该在哪里使用回调,因为我可以完成回调对事件的处理,但会导致在应用程序中创建大量事件


编写代码时,使用事件或回调应该遵循什么好方法?

事件-用于可能发生多次的事件

回访(或承诺)-对于可能发生一次的事情

例如,当您调用一个函数,因为您需要从某个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());
    }
});
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);
    }
});
function a(cb) {
    fs.readFile('a.txt', 'utf-8', (err, data) => {
        if (err) {
            return cb(err);
        }
        cb(null, 'a() data: ' + data.trim());
    });
如果没有文件,它将打印:

Error: ENOENT: no such file or directory, open 'a.txt'
Data: Contents of a.txt
如果有文件,它将打印:

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);
    });
function a() {
    return fs.readFile('a.txt', 'utf-8')
        .then(data => 'a() data: ' + data.trim());
}
它的工作原理与前面的示例完全相同

对于这个简单的例子,区别可能不是很明显,但是如果您想要一个函数来抽象一些逻辑呢

例如,对于回调:

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());
    }
});
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);
    }
});
function a(cb) {
    fs.readFile('a.txt', 'utf-8', (err, data) => {
        if (err) {
            return cb(err);
        }
        cb(null, 'a() data: ' + data.trim());
    });
它将打印此

Error: a() error: ENOENT: no such file or directory, open 'a.txt'
或者像这样:

Data: a() data: Contents of a.txt
async function x() {
    try {
        console.log('Data:', await a());
    } catch (err) {
       console.log('Error:', err);
    }
}
现在,promises的不同之处在于,在附加成功/错误处理程序之前,您可以将其存储在变量中,从函数返回,或者将其作为参数传递给其他函数。例如:

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()
函数时附加回调。你可以在别的地方做

如果您不想更改错误消息,则将使用回调:

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());
    }
});
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);
    }
});
function a(cb) {
    fs.readFile('a.txt', 'utf-8', (err, data) => {
        if (err) {
            return cb(err);
        }
        cb(null, 'a() data: ' + data.trim());
    });
并承诺:

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);
    });
function a() {
    return fs.readFile('a.txt', 'utf-8')
        .then(data => 'a() data: ' + data.trim());
}
另一个区别是,如果您有一个返回承诺的函数,您可以在
异步函数中使用一个新的
wait
关键字,如下所示:

Data: a() data: Contents of a.txt
async function x() {
    try {
        console.log('Data:', await a());
    } catch (err) {
       console.log('Error:', err);
    }
}
不能对不返回承诺的函数使用
wait

例如,当您需要读取文件
a.txt
以获取其包含的另一个文件名,然后读取该文件并打印其内容,同时在更复杂的情况下处理所有错误时,它会变得非常方便

要对节点v7.x使用
async
wait
,需要使用
--harmony
标志,请参阅:


你能帮助我理解我什么时候应该使用callback,什么时候也应该使用promise吗?@Karan通过更多关于callback和promises的示例查看我的更新答案。