node.js中的异步编程总结

    科技2024-05-06  90

    1. 阻塞与非阻塞IO

    IO即为输入(input)和输出(output) 阻塞与非阻塞的区别在于输入后,输入方能不能去做其他的事情,如果不能则为阻塞,反正则为非阻塞。 以点菜为例,如果在食堂点菜,点菜阿姨需要把饭打好后,才能处理下一个同学的点菜。而在餐厅,服务员下单后,将单传给厨房,自己则可以继续为其他顾客下单。 在这个例子中,把订单传给厨房作为输入,点菜阿姨因为自己既是下单也是打饭的,下单后需要处理完当前订单才能让下一个同学下单,因此是阻塞的。而在餐厅,服务员下单后传给厨房,剩下的事就不是自己的职责范围了,可以给其他顾客服务,因此是非阻塞的。

    2. 异步编程之callback

    调用interview方法的时候传递一个回调方法,interview执行结束的时候再调用这个回调方法,至于回调方法的参数第一个参数是err,第二个参数是返回结果都是约定俗成的

    2.1 callback示例

    interview(function (err, res) { if (err) { return console.log('cry') } console.log('smile') console.log(res) // success }) function interview(callback) { setTimeout(() => { num = Math.random() console.log(num) if (num > 0.1) { callback(null, 'success') } else { callback(new Error('fail')) } }, 500) }

    2.2 捕获异常

    按下面这样是捕获不到错误的,因为node的事件循环机制,setTimeout会开启一个事件,会是一个全新的调用栈, node会循环判断这个事件是否已结束(是否一直循环暂时还不清楚),抛出错误后系统会崩溃,不会被捕获到,如果把setTimeout去掉则可以捕获到

    try { interview(function (err, res) { if (err) { return console.log('cry') } console.log('smile') console.log(res) // success }) } catch(e) { console.log('报错了') // 这样是抓取不到错误的 } function interview(callback) { setTimeout(() => { num = Math.random() console.log(num) if (num > 0.4) { callback(null, 'success') } else { throw new Error('fail') } }, 500) }

    2.3 回调地狱

    这种回调写法虽然简单,但是会带来下面这种情况,多次嵌套会出现回调地狱,代码难以阅读

    interview(function(err, res) { if (err) { return console.log('cry') } interview(function(err, res) { if (err) { return console.log('cry') } interview(function(err, res) { if (err) { return console.log('cry') } console.log('interview success, smile') }) }) }) function interview(callback) { setTimeout(() => { num = Math.random() console.log(num) if (num > 0.4) { callback(null, 'success') } else { callback(new Error('fail')) } }, 500) }

    3. 异步编程之Promise

    在promise还没有执行结果的时候是pending状态,执行成功后是resolved状态,执行失败是rejected状态,resovle和reject都只能接收一个参数

    3.1 promise示例

    (function() { var promise = new Promise(function(resolve, reject) { setTimeout(() => { resolve(3); }, 300) }).then(function(res) { console.log(res) // 3 }).catch(function(err) { console.log(err) }) console.log(promise) // 打印状态,在浏览器Console中调试能看到状态的变化 })()

    在浏览器中调试,查看promise的状态

    3.2 promise解决callback回调地狱的问题

    var promise = interview(1) .then((res) => { return interview(2) }) .then(() => { return interview(3) }) .then(() => { console.log('三轮面试都成功了') }) .catch((err) => { // reject后的异常会被第一个catch捕获到 console.log('第' + err.round + '轮面试失败了') }) function interview(round) { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.4) { resolve('success') } else { var error = new Error('fail') error.round = round reject(error) } }, 500) }) }

    3.3 promise异步任务并行执行

    Promise .all([ interview(1), interview(2) ]) .then(() => { console.log('两个并行任务都执行成功了') }) .catch((err) => { console.log('某个任务出现了问题') })

    4. 异步编程之async-await

    await会等待后面的promise执行结束,从而达到同步的方式写异步

    4.1 async-await示例

    await后面跟着的就是promise

    (async function() { try { await interview(1) await interview(2) await interview(3) } catch(err) { return console.log('第' + err.round + '轮面试失败了') } console.log('面试成功') })() function interview(round) { return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() > 0.4) { resolve('success') } else { var error = new Error('fail') error.round = round reject(error) } }, 100) }) }

    4.1 异步任务并行执行

    (async function() { try { await Promise.all([interview(1), interview(2)]) } catch(err) { return console.log('第' + err.round + '轮面试失败了') } console.log('面试都成功了') })()
    Processed: 0.015, SQL: 8