异步编程是 Node.js 的基本组成部分:
异步编程与回调
定时器
Promise
Async 与 Await
闭包
事件循环
另一个区别是 Node.js 使用 CommonJS 模块系统,而在浏览器中,则还正在实现 ES 模块标准。
在实践中,这意味着在 Node.js 中使用 require(),而在浏览器中则使用 import。
运行全局可用的 node 命令(已安装 Node.js)并传入要执行的文件的名称。
process 模块
异步且具有非阻塞的 I/O
ode.js JavaScript 代码运行在单个线程上。 每次只处理一件事。
调用堆栈是一个 LIFO 队列(后进先出)。
事件循环不断地检查调用堆栈,以查看是否需要运行任何函数。
当执行时,它会将找到的所有函数调用添加到调用堆栈中,并按顺序执行每个函数。
每次迭代中的事件循环都会查看调用堆栈中是否有东西并执行它直到调用堆栈为空:
当调用 setTimeout() 时,浏览器或 Node.js 会启动定时器。 当定时器到期时(在此示例中会立即到期,因为将超时值设为 0),则回调函数会被放入“消息队列”中。
在消息队列中,用户触发的事件(如单击或键盘事件、或获取响应)也会在此排队,然后代码才有机会对其作出反应。 类似 onLoad 这样的 DOM 事件也如此。
事件循环会赋予调用堆栈优先级,它首先处理在调用堆栈中找到的所有东西,一旦其中没有任何东西,便开始处理消息队列中的东西。
我们不必等待诸如 setTimeout、fetch、或其他的函数来完成它们自身的工作,因为它们是由浏览器提供的,并且位于它们自身的线程中。 例如,如果将 setTimeout 的超时设置为 2 秒,但不必等待 2 秒,等待发生在其他地方。
ECMAScript 2015 引入了作业队列的概念,Promise 使用了该队列(也在 ES6/ES2015 中引入)。 这种方式会尽快地执行异步函数的结果,而不是放在调用堆栈的末尾。
在当前函数结束之前 resolve 的 Promise 会在当前函数之后被立即执行。
有个游乐园中过山车的比喻很好:消息队列将你排在队列的后面(在所有其他人的后面),你不得不等待你的回合,而工作队列则是快速通道票,这样你就可以在完成上一次乘车后立即乘坐另一趟车。
传给 process.nextTick() 的函数会在事件循环的当前迭代中(当前操作结束之后)被执行。 这意味着它会始终在 setTimeout 和 setImmediate 之前执行。
延迟 0 毫秒的 setTimeout() 回调与 setImmediate() 非常相似。 执行顺序取决于各种因素,但是它们都会在事件循环的下一个迭代中运行。
当要确保在下一个事件循环迭代中代码已被执行,则使用 nextTick()。
调用 setTimeout(() => {}, 0) 会在下一个滴答结束时执行该函数,比使用 nextTick()(其会优先执行该调用并在下一个滴答开始之前执行该函数)晚得多。
setInterval 每 n 毫秒启动一个函数,而无需考虑函数何时完成执行。
也许一个较长时间的执行会与下一次执行重叠:
setInterval 重叠
为了避免这种情况,可以在回调函数完成时安排要被调用的递归的 setTimeout:
const myFunction = () => {
// 做些事情
setTimeout(myFunction, 1000)
}
setTimeout(myFunction, 1000)
处理回调中的错误
如何处理回调的错误? 一种非常常见的策略是使用 Node.js 所采用的方式:任何回调函数中的第一个参数为错误对象(即错误优先的回调)。
如果没有错误,则该对象为 null。 如果有错误,则它会包含对该错误的描述以及其他信息。
安装所有依赖
如果项目具有 package.json 文件,则通过运行:
它会在 node_modules 文件夹(如果尚不存在则会创建)中安装项目所需的所有东西。
当使用 npm install 安装 npm 软件包时,是将其安装为依赖项。
该软件包会被自动地列出在 package.json 文件中的 dependencies 列表下(在 npm 5 之前:必须手动指定 --save)。
需要设置 --production 标志(npm install --production),以避免安装这些开发依赖项。
p
ackage.json 文件支持一种用于指定命令行任务(可通过使用以下方式运行)的格式:
npm run
npm root -g 命令会告知其在计算机上的确切位置。
package.json 文件是项目的清单。 它可以做很多完全互不相关的事情。 例如,它是用于工具的配置中心。 它也是 npm 和 yarn 存储所有已安装软件包的名称和版本的地方。
示例:
“version”: “1.0.0”
此属性遵循版本的语义版本控制记法,这意味着版本始终以 3 个数字表示:x.x.x。
第一个数字是主版本号,第二个数字是次版本号,第三个数字是补丁版本号。
这些数字中的含义是:仅修复缺陷的版本是补丁版本,引入向后兼容的更改的版本是次版本,具有重大更改的是主版本
当在应用程序中导入此软件包时,应用程序会在该位置搜索模块的导出。
当使用 npm 或 yarn 安装软件包时:
npm install
yarn add
它们不同于 dependencies,因为它们只需安装在开发机器上,而无需在生产环境中运行代码。
当使用 npm 或 yarn 安装软件包时:
npm install --dev
yarn add --dev
示例:
“browserslist”: [
“> 1%”,
“last 2 versions”,
“not ie <= 8”
]
此配置意味着需要支持使用率超过 1%(来自 CanIUse.com 的统计信息)的所有浏览器的最新的 2 个主版本,但不含 IE8 及更低的版本。
软件包版本
在上面的描述中,已经看到类似以下的版本号:〜3.0.0 或 ^0.13.0。 它们是什么意思,还可以使用哪些其他的版本说明符?
该符号指定了软件包能从该依赖接受的更新。
鉴于使用了 semver(语义版本控制),所有的版本都有 3 个数字,第一个是主版本,第二个是次版本,第三个是补丁版本,具有以下规则:
~: 如果写入的是 〜0.13.0,则只更新补丁版本:即 0.13.1 可以,但 0.14.0 不可以。
^: 如果写入的是 ^0.13.0,则要更新补丁版本和次版本:即 0.13.1、0.14.0、依此类推。
*: 如果写入的是 *,则表示接受所有的更新,包括主版本升级。
: 接受高于指定版本的任何版本。
=: 接受等于或高于指定版本的任何版本。
<=: 接受等于或低于指定版本的任何版本。
<: 接受低于指定版本的任何版本。
还有其他的规则:
无符号: 仅接受指定的特定版本。
latest: 使用可用的最新版本。
还可以在范围内组合以上大部分内容,例如:1.0.0 || >=1.1.0 <1.2.0,即使用 1.0.0 或从 1.1.0 开始但低于 1.2.0 的版本。
npm uninstall
如果使用 -S 或 --save 标志,则此操作还会移除 package.json 文件中的引用。
如果程序包是开发依赖项(列出在 package.json 文件的 devDependencies 中),则必须使用 -D 或 --save-dev 标志从文件中移除:
npm uninstall -S
npm uninstall -D
如果该软件包是全局安装的,则需要添加 -g 或 --global 标志:
npm uninstall -g
例如:
npm uninstall -g webpack
let done = true
const isItDoneYet = new Promise((resolve, reject) => {
if (done) {
const workDone = '这是创建的东西'
resolve(workDone)
} else {
const why = '仍然在处理其他事情'
reject(why)
}
})
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')
Promise.all([f1, f2])
.then(res => {
console.log('Array of results', res)
})
.catch(err => {
console.error(err)
})