从0开始Node.js - 事件循环和异步回调

466 阅读1分钟
  • 异步回调 首先我们先看一个简单的异步回调的代码例子
function asyncFun(callback) {
    setTimeout(() => callback('success'), 300);
}

asyncFun(function (e) {
    console.log('end', e);
})

我们首先定义了一个异步函数asyncFun,接受一个函数参数用于在setTimeout中异步执行,这个简单的例子没什么好说的。 但是如果在异步函数中需要跑出了一个错误呢?

function asyncFun(callback) {
    setTimeout(() => {
        if (Math.random < 0.5)
            callback('success');
        else
            throw new Error('fail')
    }, 300);
}


try {
    asyncFun(function (e) {
        console.log('end', e);
    })
} catch (err) {
    console.log('err', err)
}

这样执行可以成功catch到错误吗?

image.png 答案是并不可以,全局报错了。 因为异步调用,函数真正执行抛出错误的地方已经脱离了原来方法的调用栈,通过node.js的事件循环触发执行的。 那要抛出错误,并在外面处理错误应该如何操作?

function asyncFun(callback) {
    setTimeout(() => {
        if (Math.random < 0.5)
            callback('success');
        else
            callback(new Error('fail'))
    }, 300);
}

asyncFun(function (e) {
    if (e instanceof Error) {
        console.log('errHere', e)
    } else {
        console.log('successs', e)
    }
})

错误也通过callback抛出。现在的执行结果如下:

image.png 可以正常处理错误了。 但是这需要调用方去判断参数类型,做相应的处理。有点过于繁琐了。

所以node.js有一个关于回调函数的约定:error-first 就是如果出现error,error都是作为callback的第一个参数返回:

function asyncFun(callback) {
    setTimeout(() => {
        if (Math.random < 0.5)
            callback(null, 'success');
        else
            callback(new Error('fail'))
    }, 300);
}

asyncFun(function (e) {
    if (e) {
        console.log('errHere', e)
    } else {
        console.log('successs', e)
    }
})

这样函数调用的时候会方便很多。

  • 事件循环 Event Loop是Node.js里面很重要的概念。
    是Javascript单线程同时能保证效率的有效办法。 这里我们先不讨论EventLoop中宏任务,微任务等相关概念。我们先模拟实现一个简单的Event Loop。
const eventLoop = {
    queue: [],
    loop: function () {
        if (this.queue.length) {
            const callBack = this.queue.shift();
            callBack();
        }
        setTimeout(this.loop.bind(this), 100)
    },
    add: function (callBack) {
        this.queue.push(callBack);
    }
}

eventLoop.loop();
setTimeout(() => console.log('111'), 300);
setTimeout(() => console.log('222'), 600);

输出的结果如下:

image.png

以上简单的描述了Node.js中的异步回调和事件循环。