Node.js-实战前奏

444 阅读4分钟

1, 什么是nodejs

​ 官网说:Node.js是一个基于Chrome V8引擎的JavaScript运行环境,

​ Node.js使用了一个事件驱动,非阻塞式 I/o的模型,使其轻量有高效

认识的真谛要用自己一辈子去理解, Node.js的真谛也是一样,需要你不停的去学习理解

2,node.js和chrome不同?

​ 注意:在Node.js里面运行JavaScript跟在Chrome里运行JavaScript有什么不同?

​ 其实,在Node.js里面写JS和在Chrome里面写JS,几乎没有不一样,不一样的地方在于,Node.js没有浏览器API, 即document, window等, 加入了很多Node.jsAPI, 对开发者来说, 在Chrome里面写JavaScript控制浏览器, 在Node.js里面写JavaScript, 让你控制整个计算机。

  • node中常见拥有的API
console.log('Hello World')
console.log(Date)
console.log(Math)
console.log(setTimeout)
console.log(setInterval)
console.log(setImmediate)
// 当前运行的脚本所在的位置
console.log(__filename)
// 当前运行的脚本所在的文件目录
console.log(__dirname)
console.log(process)

3, 石头剪刀布游戏

var playerAction = process.argv[process.argv.length - 1]
console.log('玩家出了:',playerAction)

var random = Math.random() * 3;
if (random < 1) {
   var computerAction = 'rock'
} else if (random > 2) {
   var computerAction = 'scissor'
} else {
   var computerAction = 'paper'
}
console.log('电脑出了:', computerAction)
if (computerAction == playerAction ){
  console.log('平局')
} else if (
  (computerAction === 'rock' && playerAction === 'paper') ||
  (computerAction ===  'scissor' && playerAction == 'rock') ||
  (computerAction === 'paper' && playerAction === 'scissor')
) {
   console.log('你赢了')
} else {
  console.log('你输入了')
}
$ node index.js rock
玩家出了: rock
电脑出了: paper
你输入了

4,模块规范

​ 传统JS加载方式,通过<script>脚本,脚本变多的时候,需要手动管理加载顺序, 不同脚本之前的逻辑调用,需要通过全局变量的方式,不方便后续的管理,而且,有的时候,是没有HTML页面的,在JavaScript的社区推广下,CommonJS 模块规范运用而生,也在Node.js上得到广大的推广,慢慢的也影响到了浏览器端的JavaScript。

封装一下:上面的石头剪刀布游戏:

lib.js

module.exports = function (playerAction) {
  var random = Math.random() * 3;
  if (random < 1) {
    var computerAction = "rock";
  } else if (random > 2) {
    var computerAction = "scissor";
  } else {
    var computerAction = "paper";
  }
  console.log("我出了:", computerAction);
  if (computerAction == playerAction) {
    console.log("平局");
    return 0;
  } else if (
    (computerAction === "rock" && playerAction === "paper") ||
    (computerAction === "scissor" && playerAction == "rock") ||
    (computerAction === "paper" && playerAction === "scissor")
  ) {
    console.log("你赢了");
    return -1;
  } else {
    console.log("你输入了");
    return 1;
  }
};

index.js

var playerAction = process.argv[process.argv.length - 1]
const game = require('./lib')
let count = 0;
process.stdin.on('data', e=> {
   const playerAction = e.toString().trim();
   const result = game(playerAction)
   console.log(result)
   if (result == -1) {
     count++;
   }
   if (count === 3) {
     console.log('你太厉害了,我不玩了')
     process.exit()
   }
})

5, 非阻塞I/O

注意:对比glob方法,读取目录下的文件,查看耗时

const glob = require('glob')
// 阻塞代码  3248ms
var result = null;
console.time('glob')
result = glob.sync(__dirname + '/**/*')
console.timeEnd('glob')
console.log(result)

// 异步,回调  2ms
console.time('glob-2')
glob(__dirname + '/**/*', function(err, res) {
    result = res;
    console.log('get result', result)
})
console.timeEnd('glob-2')

6, 异步编程-callback

  • 回调函数格式规范

    error-first-callback

    node-style-callback

  • 第一个参数是error, 后面的参数才是结果

interview(function(res) {
   if( res instanceof Error) {
     return console.log('cry')
   }
   console.log('smile')
})

function interview(callback) {
   setTimeout(() => {
     if(Math.random() < 0.8) {
        callback('success')
     } else {
       callback(new Error('fail'))
     }
   }, 500)
}

异步流程控制:

事件循环:

const eventloop = {
  queue : [],
  loop() {
     while (this.queue.length) {
       var callback = this.queue.shift();
       callback()
     }
    setTimeout(this.loop.bind(this), 50)
  },
  add(callback) {
    this.queue.push(callback);
  }
}
eventloop.loop()

setTimeout(() => {
   eventloop.add(function() {
     console.log(1)
   })
}, 500)

setTimeout(() => {
  eventloop.add(function() {
    console.log(2)
  })
}, 800)

7, promise

  • 当前事件循环得不到的结果,但未来的事件循环会给到你结果

  • 是一个状态机

    pending fultilled/resolved rejected

    .then 和 .catch

    1, resolved 状态的promise会调用后面的第一个.then

    2, rejected状态的Promise会回调后面的第一个.catch

    3, 任何一个rejected状态且后面没有.catch的promise, 都会造成浏览器/node环境的全局错误

var promise = new Promise(function(resolve, reject) {
     setTimeout(() => {
       resolve(3)
     }, 500)
}).then(res => {
   console.log(res)
}).catch(err => {
  console.log(err)
})
console.log(promise)

setTimeout(() => {
  console.log(promise)
}, 800)

注意:执行then和catch会返回一个新的Promise, 该Promise最终状态根据then和catch的回调函数的执行结果决定

  • 如果回调函数最终是throw, 该promise是rejected状态。
  • 如果回调函数最终是return, 该promise是resolved状态
  • 如果回调函数最终return了一个Promise, 该Promsie会和回调函数return的Promise状态保持一致
;(function() {
   var promise = interview(1)
        .then(() => {
          return interview(2)
        })
        .then(() => {
          return interview(3)
        })
        .then(() => {
          console.log('smile')
        })
        .catch((err) => {
          console.log('cry at ' + err.round + ' round')
        })

   function interview(round) {
      return new Promise((resolve, reject) => {
         setTimeout(() => {
           if (Math.random() > 0.2) {
              resolve('success')
           } else {
              var error = new Error('fail')
              error.round = round
              reject(error)
           }
         }, 500)
      })
   }
})()

Promise.all的演示

// var promise = new Promise(function(resolve, reject) {
//      setTimeout(() => {
//        resolve(3)
//      }, 500)
// }).then(res => {
//    console.log(res)
// }).catch(err => {
//   console.log(err)
// })
// console.log(promise)

// setTimeout(() => {
//   console.log(promise)
// }, 800)

;(function() {
  //  var promise = interview(1)
  //       .then(() => {
  //         return interview(2)
  //       })
  //       .then(() => {
  //         return interview(3)
  //       })
  //       .then(() => {
  //         console.log('smile')
  //       })
  //       .catch((err) => {
  //         console.log('cry at ' + err.round + ' round')
  //       })
   Promise
       .all([
         interview('geekbang'),
         interview('tencent')
       ])
       .then(() => {
         console.log('smile')
       })
       .catch((err) => {
          console.log('cry for ' + err.name)
       })
   function interview(name) {
      return new Promise((resolve, reject) => {
         setTimeout(() => {
           if (Math.random() > 0.2) {
              resolve('success')
           } else {
              var error = new Error('fail')
              error.name = name
              reject(error)
           }
         }, 500)
      })
   }
})()

8, async/await

async function 是Promise的语法糖封装

异步编程的终极方案, 以同步的方式写异步

console.log(async function() {
   throw new Error('4')
}())

console.log(function() {
   return new Promise((resolve, reject) => {
      reject(new Error('4'))
   })
}())
  • await关键字可以暂停 async function的执行
  • await关键字可以以同步的写法获取promise的执行结果
  • try-catch可以获取await所得到的错误
;(function() {
   const result = async function() {
      try {
         var content = await new Promise((resolve, reject) => {
            setTimeout(() => {
               reject(new Error('8'))
            }, 500)
         })
      } catch(e) {
         console.log('err', e.message)
      }
      console.log(content)
      return 4;
}()
   setTimeout(() => {
   console.log(result)
   }, 800)
})()
undefined
VM410:10 err 8
VM410:12 undefined
VM410:16 Promise {<fulfilled>: 4}

注意:对之前的代码进行async/await进行修改

;(async function() {
    try {
       await interview('1')
       await interview('2')
       await interview('3')
      // 如果想做并行的异步
      // await Promise.all([interview(1), interview(2)]) 
      
    } catch(e) {
       return console.log('cry at ' + e.name)
    }
    console.log('smile')
})()
function interview(name) {
  return new Promise((resolve, reject) => {
     setTimeout(() => {
       if (Math.random() > 0.8) {
          resolve('success')
       } else {
          var error = new Error('fail')
          error.name = name
          reject(error)
       }
     }, 500)
  })
}