【亚洲必赢官网】的异步编程实践,处理树形结构的多少个情景与方案

浅谈 JavaScript 处理树形结构的多少个情景与方案

2015/06/18 · CSS ·
树形结构

原稿出处:
工业聚(@工业聚)   

node.js下when.js 的异步编程实践,node.jswhen.js

即使一个事情场景:

透过rss地址,获取rss并保留于文件,rss地址保存于文件中。

做到这一场地的政工需求形成3个职责:

1.从文件中读取rss地址。

2.获取rss。

3.保留于文件。

终极将那五个职分进行结合。

准备:

存放rss地址的文本,address.txt。

 
任务1:

读取rss地址文件的始末并通过callback再次来到。

复制代码 代码如下:

var getRssAddress = function(path, callback) {
  fs.readFile(path, {encoding: ‘utf8’}, function (err, data) {
    callback(err, data);
  });
}

任务2:

 通过rss地址get到rss,并通过callback再次来到错误或数额。

复制代码 代码如下:

var getRss = function(url, callback) {
  var data = ”;
  http.get(url, function(res) {
    res.on(‘data’, function(chrunk) {
      data += chrunk;
    });
    res.on(‘end’, function() {
      callback(null, data);
    });
  }).on(‘error’, function(err) {
    callback(err, null);
  });
}

 

任务3:

将rss保存于文件并经过callback再次来到错误。

复制代码 代码如下:

var saveRss = function(data, callback) {
  fs.writeFile(‘rss.txt’, data, ‘utf8’, function(err) {
    callback(err);
  });
}

整合:

复制代码 代码如下:

getRssAddress(‘address.txt’, function(err, data) {
  if(err) {
    console.log(err);
    return;
  }
  getRss(data, function(err, data) {
【亚洲必赢官网】的异步编程实践,处理树形结构的多少个情景与方案。    if(err) {
      console.log(err);
      return;
    }
    saveRss(data, function(err) {
      if(err) console.log(err);
    });
  });
});

上面的代码是全异步处理,使用最广大的callback处理异步逻辑的回来,好处是业内写法,大家都能不难接受;坏处是耦合性太强,处理相当麻烦,代码不直观,更加是拍卖工作逻辑复杂和拍卖职分多的气象,层层的callback会令人眼冒火星,代码难以有限支撑。

Promise/A规范的兑现之一when.js正是本着如此的难题域。

让大家来看一下改造后的代码。

任务1:

复制代码 代码如下:

var getRssAddress = function(path) {
    var deferred = when.defer();
      fs.readFile(path, {encoding: ‘utf8’}, function (err, data) {
        if (err) deferred.reject(err);
        deferred.resolve(data);
      });

    return deferred.promise;
}

 
任务2:

复制代码 代码如下:

var getRss = function(url) {
  var deferred = when.defer();
    var data = ”;
    http.get(url, function(res) {
      res.on(‘data’, function(chrunk) {
        data += chrunk;
      });
      res.on(‘end’, function() {
        deferred.resolve(data);
      });
    }).on(‘error’, function(err) {
      deferred.reject(err);
    });

    return deferred.promise;
}

任务3:

复制代码 代码如下:

var saveRss = function(data) {
  var deferred = when.defer();
  fs.writeFile(‘rss.txt’, data, ‘utf8’, function(err) {
    if(err) deferred.reject(err);
    deferred.resolve();
  });

  return deferred.promise;
}

 

整合:

复制代码 代码如下:

getRssAddress(‘address.txt’)
  .then(getRss)
  .then(saveRss)
  .catch(function(err) {
    console.log(err);
  });

解释:

promise/A规范定义的“Deferred/Promise”模型就是“发布/订阅者”模型,通过Deferred对象发表事件,可以是成功resolve事件,或者是失利reject事件;通过Promise对象进行对应形成或破产的订阅。

在Promises/A规范中,每个任务都有两种情景:默许(pending)、完结(fulfilled)、失利(rejected)。

1.默认状态可以一边转移到落成意况,那么些进程叫resolve,对应的格局是deferred.resolve(promiseOrValue);

2.默许状态还足以单方面转移到破产状态,这一个历程叫reject,对应的点子是deferred.reject(reason);

3.默许状态时,还足以经过deferred.notify(update)来发表职分履行新闻,如进行进度;

4.动静的变换是四回性的,一旦职分由开首的pending转为其余景况,就会跻身到下一个义务的执行进程中。

依照上边的代码。

因而when.defer定义一个deferred对象。

var deferred = when.defer();
异步数据得到成功后,宣布一个到位事件。

deferred.resolve(data);
异步数据得到失利后,发表一个小败事件。

deferred.reject(err);
同时重返Promise对象作为订阅使用。

return deferred.promise;
订阅是经过Promise对象的then方法开展完结/失败/公告的订阅。

getRssAddress(‘address.txt’)
  .then(getRss)
then有多个参数,分别是onFulfilled、onRejected、onProgress

promise.then(onFulfilled, onRejected, onProgress)
上一个职分被resolve(data),onFulfilled函数就会被触发,data作为它的参数.

上一个任务被reject(reason),那么onRejected就会被触发,收到reason。

此外时候,onFulfilled和onRejected都只有其一可以被触发,并且只触发一回。

对于拍卖非凡,when.js也提供了最为方便的方法,then能传递错误,三个任务串行执行时,大家能够只在结尾一个then定义onRejected。也可以在最后一个then的背后调用catch函数捕获任何一个职分的相当。

这么写法不难明了。

复制代码 代码如下:

getRssAddress(‘address.txt’)
  .then(getRss)
  .then(saveRss)
  .catch(function(err) {
    console.log(err);
  });

Promise给异步编程带来了伟大的方便,可以让大家注意于单个职分的落到实处而不会深陷金字塔厄运,以上代码仅仅是主旨使用,when.js提供的意义远远不止本文提到的这个,具体参照官方API。

的异步编程实践,node.jswhen.js
假诺一个业务场景:
通过rss地址,获取rss并保留于文件,rss地址保存于文件中。
落成本场馆的…

姓名:岳沁

Promise in js

前言

不久前,Mac 下有名软件 Homebrew
的小编,因为没解出来二叉树翻转的白板算法题,惨遭 谷歌拒绝,继而引发推文(Tweet)热议。

在 JavaScript 中也有诸多树形结构。比如 DOM
树,省市区地方联动,文件目录等; JSON 本身就是树形结构。

重重前端面试题也跟树形结构的关于,比如在浏览器端写遍历 DOM
树的函数,比如在 nodejs 运行时遍历文件目录等。

此地演示用 JavaScript 遍历树形结构的三种政策。

学号:17101223458

回调函数真正的标题在于她剥夺了俺们利用 return 和 throw
那几个重点字的力量。而 Promise 很好地解决了这一切。

场景1:遍历 DOM 树

转载自:

2015 年 6 月,ECMAScript 6
的标准版到底揭橥了。

方案1:递归方式

JavaScript

function walkDom(node, callback) { if (node === null) {
//判断node是还是不是为null return } callback(node) //将node自身传入callback
node = node.firstElementChild //改变node为其子元素节点 while (node) {
walkDom(node, callback) //即使存在子元素,则递归调用walkDom node =
node.nextElementSibling //从头到尾遍历元素节点 } } walkDom(document,
function(node) {console.count()}) //包括document节点
document.querySelectorAll(‘*’).length
//数量比地点输出的少1,因为不带有document节点

1
2
3
4
5
6
7
8
9
10
11
12
13
function walkDom(node, callback) {
    if (node === null) { //判断node是否为null
        return
    }
    callback(node) //将node自身传入callback
    node = node.firstElementChild //改变node为其子元素节点
    while (node) {
        walkDom(node, callback) //如果存在子元素,则递归调用walkDom
        node = node.nextElementSibling //从头到尾遍历元素节点
    }
}
walkDom(document, function(node) {console.count()}) //包含document节点
document.querySelectorAll(‘*’).length //数量比上面输出的少1,因为不包含document节点

将上述代码黏贴到任意页面的控制台 console 中实施。

【嵌牛导读】:ES6 原生提供了 Promise 对象。

ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript
的贯彻。ES6 的靶子,是驱动 JavaScript
语言能够用来编排大型的复杂的应用程序,成为商家级开发语言。

方案2:循环形式

JavaScript

function walkDom(node, callback) { if (node === null) { return } var
stack = [node] //存入数组 var target while(stack.length) {
//数总监度不为0,继续循环 target = stack.shift() //取出元素
callback(target) //传入callback Array.prototype.push.apply(stack,
target.children) //将其子元素一股脑推入stack,扩充长度 } }
walkDom(document, function(node) {console.count()}) //包蕴document节点
document.querySelectorAll(‘*’).length
//数量比地点输出的少1,因为不含有document节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function walkDom(node, callback) {
    if (node === null) {
        return
    }
    var stack = [node] //存入数组
    var target
    while(stack.length) { //数组长度不为0,继续循环
        target = stack.shift() //取出元素
        callback(target) //传入callback
        Array.prototype.push.apply(stack, target.children) //将其子元素一股脑推入stack,增加长度
    }
}
walkDom(document, function(node) {console.count()}) //包含document节点
document.querySelectorAll(‘*’).length //数量比上面输出的少1,因为不包含document节点

在循环方式中,shift方法可以换成pop,从尾部取出元素;push方法可以换成unshift从底部添法郎素。不相同的依次,影响了是「广度优先」仍旧「深度优先」。

所谓
Promise,就是一个目标,用来传递异步操作的新闻。它代表了某个以后才会驾驭结果的事件(平时是一个异步操作),并且这些事件提供联合的
API,可供进一步处理。

概念

场景2:在 nodejs 运行时里遍历文件目录

【嵌牛鼻子】:Promise

ES6 原生提供了 Promise 对象。

子场景1:同步格局

【嵌牛提问】:怎么着增强Promise功效?

所谓
Promise,就是一个目的,用来传递异步操作的新闻。它表示了某个未来才会知晓结果的事件(常常是一个异步操作),并且那几个事件提供统一的
API,可供进一步处理。

方案1:递归

JavaScript

var fs = require(‘fs’) var Path = require(‘path’) function
readdirs(path) { var result = { //构造文件夹数据 path: path, name:
Path.basename(path), type: ‘directory’ } var files =
fs.readdirSync(path) //获得文件目录下的装有文件名 result.children =
files.map(function(file) { var subPath = Path.resolve(path, file)
//拼接为相对路径 var stats = fs.statSync(subPath) //得到文件音信目的 if
(stats.isDirectory()) { //判断是不是为文件夹类型 return readdirs(subPath)
//递归读取文件夹 } return { //构造文件数量 path: subPath, name: file,
type: ‘file’ } }) return result //重临数据 } var cwd = process.cwd() var
tree = readdirs(cwd) fs.writeFileSync(Path.join(cwd, ‘tree.json’),
JSON.stringify(tree)) //保存在tree.json中,去查看吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var fs = require(‘fs’)
var Path = require(‘path’)
 
function readdirs(path) {
    var result = { //构造文件夹数据
        path: path,
        name: Path.basename(path),
        type: ‘directory’
    }
    var files = fs.readdirSync(path) //拿到文件目录下的所有文件名
    result.children = files.map(function(file) {
        var subPath = Path.resolve(path, file) //拼接为绝对路径
        var stats = fs.statSync(subPath) //拿到文件信息对象
        if (stats.isDirectory()) { //判断是否为文件夹类型
            return readdirs(subPath) //递归读取文件夹
        }
        return { //构造文件数据
            path: subPath,
            name: file,
            type: ‘file’
        }
    })
    return result //返回数据
}
 
var cwd = process.cwd()
var tree = readdirs(cwd)
fs.writeFileSync(Path.join(cwd, ‘tree.json’), JSON.stringify(tree)) //保存在tree.json中,去查看吧

将地方的代码保存在 tree.js 中,然后在脚下文件夹打开命令行,输入node
tree.js,目录音信保存在生成tree.json文件中。

【嵌牛正文】:

Promise 对象有以下多个特色。

方案2:循环

JavaScript

var fs = require(‘fs’) var Path = require(‘path’) function
readdirs(path) { var result = { //构造文件夹数据 path: path, name:
Path.basename(path), type: ‘directory’ } var stack = [result]
//生成一个栈数组 while (stack.length) { //假如数组不为空,读取children
var target = stack.pop() //取出文件夹对象 var files =
fs.readdirSync(target.path) //获得文件名数组 target.children =
files.map(function(file) { var subPath = Path.resolve(target.path, file)
//转化为相对路径 var stats = fs.statSync(subPath) //获得文件音信目标 var
model = { //构造文件数据结构 path: subPath, name: file, type:
stats.isDirectory() ? ‘directory’ : ‘file’ } if (model.type ===
‘directory’) { stack.push(model) //假若是文件夹,推入栈 } return model
//重回数据模型 }) } return result //重回整个数据结果 } var cwd =
process.cwd() var tree = readdirs(cwd) fs.writeFileSync(Path.join(cwd,
‘tree.json’), JSON.stringify(tree)) //保存在tree.json中,去查看吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var fs = require(‘fs’)
var Path = require(‘path’)
 
function readdirs(path) {
    var result = { //构造文件夹数据
        path: path,
        name: Path.basename(path),
        type: ‘directory’
    }
    var stack = [result] //生成一个栈数组
    while (stack.length) { //如果数组不为空,读取children
        var target = stack.pop() //取出文件夹对象
        var files = fs.readdirSync(target.path) //拿到文件名数组
        target.children = files.map(function(file) {
            var subPath = Path.resolve(target.path, file) //转化为绝对路径
            var stats = fs.statSync(subPath) //拿到文件信息对象
            var model = { //构造文件数据结构
                path: subPath,
                name: file,
                type: stats.isDirectory() ? ‘directory’ : ‘file’
            }
            if (model.type === ‘directory’) {
                stack.push(model) //如果是文件夹,推入栈
            }
            return model //返回数据模型
        })
    }
    return result //返回整个数据结果
}
 
var cwd = process.cwd()
var tree = readdirs(cwd)
fs.writeFileSync(Path.join(cwd, ‘tree.json’), JSON.stringify(tree)) //保存在tree.json中,去查看吧

循环策略中的pop跟shift,push跟unshift也足以沟通以调整优先级,甚至用可以用splice方法更小巧的操纵stack数组。循环方式比递归形式更可控。

Promise in js

(1)对象的场所不受外界影响。Promise
对象表示一个异步操作,有两种状态:Pending(进行中)、Resolved(已到位,又称
Fulfilled)和
Rejected(已破产)。只有异步操作的结果,可以操纵当前是哪个种类景况,任何其余操作都心有余而力不足转移那个景况。这也是
Promise 那个名字的缘故,它的斯洛伐克(Slovak)语意思就是「承诺」,表示其他手段不能更改。

子场景2:异步方式

回调函数真正的题材在于她剥夺了我们运用 return 和 throw
那一个关键字的力量。而 Promise 很好地解决了这一体。

(2)一旦状态改变,就不会再变,任什么日期候都可以赢得那几个结果。Promise
对象的气象改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为
Rejected。只要那二种状态时有暴发,状态就死死了,不会再变了,会直接维持这些结果。尽管改变一度发出了,你再对
Promise
对象添加回调函数,也会即时赢得这几个结果。那与事件(伊夫nt)完全差距,事件的风味是,倘诺你失去了它,再去监听,是得不到结果的。

方案1:过程式 Promise

JavaScript

var fs = require(‘fs’) var Path = require(‘path’)
//promise包装的fs.stat方法 var stat = function(path) { return new
Promise(function(resolve, reject) { fs.stat(path, function(err, stats) {
err ? reject(err) : resolve(stats) }) }) } //promise包装的fs.readdir方法
var readdir = function(path) { return new Promise(function(resolve,
reject) { fs.readdir(path, function(err, files) { err ? reject(err) :
resolve(files) }) }) } //promise包装的fs.writeFile var writeFile =
function(path, data) { return new Promise(function(resolve, reject) {
fs.writeFile(path, JSON.stringify(data || ”), function(err) { err ?
reject(err) : resolve }) }) } function readdirs(path) { return
readdir(path) //异步读取文件夹 .then(function(files) { //获得文件名列表
var promiseList = files.map(function(file) { //遍历列表 var subPath =
Path.resolve(path, file) //拼接为相对路径 return stat(subPath)
//异步读取文件音讯 .then(function(stats) { //拿到文件新闻//是文本夹类型的,继续读取目录,否则重回数据 return stats.isDirectory()
? readdirs(subPath) : { path: subPath, name: file, type: ‘file’ } }) })
return Promise.all(promiseList) //等待所有promise已毕 })
.then(function(children) { //得到含有所有数据的children数组 return {
//再次回到结果 path: path, name: Path.basename(path), type: ‘directory’,
children: children } }) } var cwd = process.cwd() readdirs(cwd)
.then(writeFile.bind(null, Path.join(cwd, ‘tree.json’)))
//保存在tree.json中,去查看吧 .catch(console.error.bind(console))
//出错了就输出错误日志查看原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
var fs = require(‘fs’)
var Path = require(‘path’)
//promise包装的fs.stat方法
var stat = function(path) {
    return new Promise(function(resolve, reject) {
        fs.stat(path, function(err, stats) {
            err ? reject(err) : resolve(stats)
        })
    })
}
//promise包装的fs.readdir方法
var readdir = function(path) {
    return new Promise(function(resolve, reject) {
        fs.readdir(path, function(err, files) {
            err ? reject(err) : resolve(files)
        })
    })
}
//promise包装的fs.writeFile
var writeFile = function(path, data) {
    return new Promise(function(resolve, reject) {
        fs.writeFile(path, JSON.stringify(data || ”), function(err) {
            err ? reject(err) : resolve
        })
    })
}
 
function readdirs(path) {
    return readdir(path) //异步读取文件夹
    .then(function(files) { //拿到文件名列表
        var promiseList = files.map(function(file) { //遍历列表
            var subPath = Path.resolve(path, file) //拼接为绝对路径
            return stat(subPath) //异步读取文件信息
            .then(function(stats) { //拿到文件信息
                //是文件夹类型的,继续读取目录,否则返回数据
                return stats.isDirectory() ?
                readdirs(subPath) : {
                    path: subPath,
                    name: file,
                    type: ‘file’
                }
            })
        })
        return Promise.all(promiseList) //等待所有promise完成
    })
    .then(function(children) { //拿到包含所有数据的children数组
        return { //返回结果
            path: path,
            name: Path.basename(path),
            type: ‘directory’,
            children: children
        }
    })
}
 
var cwd = process.cwd()
 
readdirs(cwd)
.then(writeFile.bind(null, Path.join(cwd, ‘tree.json’))) //保存在tree.json中,去查看吧
.catch(console.error.bind(console)) //出错了就输出错误日志查看原因

地方的函数都能做事,但都是一个个function的调用,显得太「进度式」;

能仍然不能够用面向对象的章程来写吗?

本来可以。

实际面向对象的写法,更清楚。

为了进一步语义化,以及增显逼格。

咱俩用 ES6 的 class 来写这么些树形结构类。

2015 年 6 月,ECMAScript 6
的标准版好不不难公布了。

有了 Promise
对象,就可以将异步操作以同步操作的流程表明出来,防止了稀有嵌套的回调函数。其余,Promise
对象提供统一的接口,使得控制异步操作尤其便于。

方案2:ES6-class + ES6-Promise

JavaScript

import fs from ‘fs’ import {join, resolve, isAbsolute, basename,
extname, dirname, sep} from ‘path’ /** * 获取目录下的拥有文件 *
@param {string} path * @return {promise} resolve files || reject error
*/ let readdir = (path) => { return new Promise((resolve, reject)
=> { fs.readdir(path, (err, files) => { err ? reject(err) :
resolve(files) }) }) } /** * 将data写入文件 * @param {string} path
路径 * @param {data} data * @return {promise} resolve path || reject
error */ let writeFile = (path, data) => { return new
Promise((resolve, reject) => { fs.writeFile(path, data, (err) => {
err ? reject(err) : resolve(path) }) }) } /** * 获取文件属性 *
@param {string} path * @return {promise} resolve stats || reject error
*/ let stat = (path) => { return new Promise((resolve, reject) =>
{ fs.stat(path, (err, stats) => { err ? reject(err) : resolve(stats)
}) }) } /** * 判断path是不是存在 * @param {string} path 路径 *
@return {promise} resolve exists */ let exists = (path) => { return
new Promise((resolve) => fs.exists(path, resolve)) } //文档类 class
Document { constructor(path) { this.path = path this.name =
basename(path) } //存在性判断 exists() { return exists(this.path) }
//异步获取文件新闻 stat() { return stat(this.path) } //输出骨干数据
json() { return JSON.stringify(this) }
//将基本数据保存在path路径的文本中 saveTo(path) { if (isAbsolute(path))
{ return writeFile(path, this.json()) } return
writeFile(resolve(this.path, path), this.json()) } }
//文件类,继承自文档类 class File extends Document { constructor(path) {
super(path) //必须先调用超类构造方法 this.type = ‘file’ //type 为 file
this.extname = extname(path) //新增扩张名 } //写入数据 write(data = ”)
{ return writeFile(this.path, data) } //其他文件特有法子如 read unlink
等 } //文件夹类,继承自文档类 class Directory extends Document {
constructor(path) { super(path) //必须先调用超类构造方法 this.type =
‘directory’ } //读取当前文件夹 readdir() { return readdir(this.path)
//读取目录 .then((files) => { //获得文件名列表 let promiseList =
files.map((file) => { let subPath = resolve(this.path, file)
//拼接为相对路径 return stat(subPath) //获取文件音讯 .then((stats) =>
{ //根据文件新闻,归类为Directory或File类型 return stats.isDirectory() ?
new Directory(subPath) : new File(subPath) }) }) return
Promise.all(promiseList) }) .then((children) => { //得到children数组
this.children = children //保存children属性 return children
//重返children }) } //深度读取文件目录 readdirs() { return
this.readdir() //读取当前文件夹 .then((children) => { //获得children
let promiseList = [] children.map((child) => { if (child instanceof
Directory) { //是文本夹实例,继续深度读取文件目录
promiseList.push(child.readdirs()) } }) return Promise.all(promiseList)
//等待所有子元素深度读取目录落成 }) .then(() => this) //重临this }
//其余文件夹特有方法如 addFile removeFile addDir remveDir 等 } let cwd =
process.cwd() new Directory(cwd) .readdirs() .then((tree) => {
tree.saveTo(‘tree.json’) //让它自己保留在tree.json里 })
.catch(console.error.bind(console)) //输出荒唐日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import fs from ‘fs’
import {join, resolve, isAbsolute, basename, extname, dirname, sep} from ‘path’
 
/**
* 获取目录下的所有文件
* @param {string} path
* @return {promise} resolve files || reject error
*/
let readdir = (path) => {
    return new Promise((resolve, reject) => {
        fs.readdir(path, (err, files) => {
            err ? reject(err) : resolve(files)
        })
    })
}
 
/**
* 将data写入文件
* @param {string} path 路径
* @param {data} data
* @return {promise} resolve path || reject error
*/
let writeFile = (path, data) => {
    return new Promise((resolve, reject) => {
        fs.writeFile(path, data, (err) => {
            err ? reject(err) : resolve(path)
        })
    })
}
 
/**
* 获取文件属性
* @param {string} path
* @return {promise} resolve stats || reject error
*/
let stat = (path) => {
    return new Promise((resolve, reject) => {
        fs.stat(path, (err, stats) => {
            err ? reject(err) : resolve(stats)
        })
    })
}
 
/**
* 判断path是否存在
* @param {string} path 路径
* @return {promise} resolve exists
*/
let exists = (path) => {
    return new Promise((resolve) => fs.exists(path, resolve))
}
 
//文档类
class Document {
    constructor(path) {
        this.path = path
        this.name = basename(path)
    }
    //存在性判断
    exists() {
        return exists(this.path)
    }
    //异步获取文件信息
    stat() {
        return stat(this.path)
    }
    //输出基本数据
    json() {
        return JSON.stringify(this)
    }
    //将基本数据保存在path路径的文件中
    saveTo(path) {
        if (isAbsolute(path)) {
            return writeFile(path, this.json())
        }
        return writeFile(resolve(this.path, path), this.json())
    }
}
 
//文件类,继承自文档类
class File extends Document {
    constructor(path) {
        super(path) //必须先调用超类构造方法
        this.type = ‘file’ //type 为 file
        this.extname = extname(path) //新增扩展名
    }
    //写入数据
    write(data = ”) {
        return writeFile(this.path, data)
    }
    //其他文件特有方法如 read unlink 等
}
 
//文件夹类,继承自文档类
class Directory extends Document {
    constructor(path) {
        super(path) //必须先调用超类构造方法
        this.type = ‘directory’
    }
    //读取当前文件夹
    readdir() {
        return readdir(this.path) //读取目录
        .then((files) => { //拿到文件名列表
            let promiseList = files.map((file) => {
                let subPath = resolve(this.path, file) //拼接为绝对路径
                return stat(subPath) //获取文件信息
                .then((stats) => {
                    //根据文件信息,归类为Directory或File类型
                    return stats.isDirectory() ?
                    new Directory(subPath) :
                    new File(subPath)
                })
            })
            return Promise.all(promiseList)
        })
        .then((children) => { //拿到children数组
            this.children = children //保存children属性
            return children //返回children
        })
    }
    //深度读取文件目录
    readdirs() {
        return this.readdir() //读取当前文件夹
        .then((children) => { //拿到children
            let promiseList = []
            children.map((child) => {
                if (child instanceof Directory) { //是文件夹实例,继续深度读取文件目录
                    promiseList.push(child.readdirs())
                }
            })
            return Promise.all(promiseList) //等待所有子元素深度读取目录完毕
        })
        .then(() => this) //返回this
    }
    //其他文件夹特有方法如 addFile removeFile addDir remveDir 等
}
 
let cwd = process.cwd()
 
new Directory(cwd)
.readdirs()
.then((tree) => {
    tree.saveTo(‘tree.json’) //让它自己保存在tree.json里
})
.catch(console.error.bind(console)) //输出错误日志

因为脚下 JavaScript 引擎对 ES6
的支撑度还不够,所以上述代码不可以直接运行。可以因而以下二种形式来说南陈码能不可能跑起来。

先是种,先 npm install -g bable 全局安装 babel 工具,再以 babel-node
tree.js的措施代替 node tree.js 来运行上述代码。

第两种,将上述代码黏贴到  ES5
的代码,将代码保存在 tree.js文件中,以 ES5 的方式举行。

ECMAScript 是 JavaScript 语言的国际标准,JavaScript 是 ECMAScript
的贯彻。ES6 的靶子,是驱动 JavaScript
语言可以用来编排大型的错综复杂的应用程序,成为商家级开发语言。

Promise 也有一部分通病。首先,不能够收回
Promise,一旦新建它就会应声施行,无法中途取消。其次,即使不设置回调函数,Promise
内部抛出的失实,不会反应到表面。第三,当远在 Pending
状态时,不可能获知近期开展到哪一个阶段(刚刚早先依旧即将落成)。

结语

以上就是我领悟的一些用 JavaScript
处理树形结构的三种方法,希望看今后对您有帮衬。

1 赞 4 收藏
评论

亚洲必赢官网 1

概念

var promise = new Promise(function(resolve, reject) {

ES6 原生提供了 Promise 对象。

if (/* 异步操作成功 */){

所谓
Promise,就是一个目的,用来传递异步操作的新闻。它代表了某个未来才会了然结果的风云(平日是一个异步操作),并且那么些事件提供联合的
API,可供进一步处理。

resolve(value);

Promise 对象有以下几个性状。

} else {

(1)对象的动静不受外界影响。Promise
对象表示一个异步操作,有二种处境:Pending(举办中)、Resolved(已到位,又称
Fulfilled)和
Rejected(已破产)。唯有异步操作的结果,可以操纵当前是哪个种类情况,任何其他操作都无法改变那个情况。那也是
Promise 这几个名字的案由,它的爱尔兰语意思就是「承诺」,表示其他手段不可能更改。

reject(error);

(2)一旦状态改变,就不会再变,任何时候都可以取得这一个结果。Promise
对象的景况改变,唯有二种可能:从 Pending 变为 Resolved 和从 Pending 变为
Rejected。只要那两种情形时有暴发,状态就死死了,不会再变了,会直接维持这些结果。即便改变一度发出了,你再对
Promise
对象添加回调函数,也会应声赢得那么些结果。这与事件(伊芙nt)完全分裂,事件的特点是,即便你错过了它,再去监听,是得不到结果的。

}

有了 Promise
对象,就足以将异步操作以同步操作的流水线表达出来,防止了千载难逢嵌套的回调函数。其它,Promise
对象提供联合的接口,使得控制异步操作尤其不难。

});

Promise 也有局部败笔。首先,不可能取消Promise,一旦新建它就会立即施行,不可以中途撤消。其次,假若不安装回调函数,Promise
内部抛出的荒谬,不会反馈到表面。第三,当远在 Pending
状态时,无法获知方今进展到哪一个阶段(刚刚起先依旧即将成功)。

promise.then(function(value) {

var promise = new Promise(function(resolve, reject) {

// success

if (/* 异步操作成功 */){

}, function(value) {

resolve(value);

// failure

} else {

});

reject(error);

Promise 构造函数接受一个函数作为参数,该函数的五个参数分别是 resolve
方法和 reject 方法。

}

假如异步操作成功,则用 resolve 方法将 Promise
对象的景观,从「未到位」变为「成功」(即从 pending 变为 resolved);

});

一旦异步操作战败,则用 reject 方法将 Promise
对象的情事,从「未到位」变为「战败」(即从 pending 变为 rejected)。

promise.then(function(value) {

基本的 api

// success

Promise.resolve()

}, function(value) {

Promise.reject()

// failure

Promise.prototype.then()

});

Promise.prototype.catch()

Promise 构造函数接受一个函数作为参数,该函数的多少个参数分别是 resolve
方法和 reject 方法。

Promise.all()    // 所有的达成

如果异步操作成功,则用 resolve 方法将 Promise
对象的情状,从「未成功」变为「成功」(即从 pending 变为 resolved);

var p = Promise.all([p1,p2,p3]);

一旦异步操作败北,则用 reject 方法将 Promise
对象的景况,从「未成功」变为「败北」(即从 pending 变为 rejected)。

Promise.race()        // 竞速,落成一个即可

基本的 api

进阶

Promise.resolve()

promises 的奇幻在于给予我们之前的 return 与 throw,每个 Promise
都会提供一个 then() 函数,和一个 catch(),实际上是 then(null, …)
函数,

Promise.reject()

somePromise().then(functoin(){

Promise.prototype.then()

// do something

Promise.prototype.catch()

});

Promise.all()    // 所有的到位

大家可以做三件事,

var p = Promise.all([p1,p2,p3]);

  1. return 另一个 promise

  2. return 一个一头的值 (或者 undefined)

  3. throw 一个联合万分 ` throw new Eror(”);`

  4. 装进同步与异步代码

Promise.race()      // 竞速,落成一个即可

“`

进阶

new Promise(function (resolve, reject) {

promises 的奇妙在于给予大家在此之前的 return 与 throw,每个 Promise
都会提供一个 then() 函数,和一个 catch(),实际上是 then(null, …)
函数,

resolve(someValue);

somePromise().then(functoin(){

});

// do something

“`

});

写成

大家可以做三件事,

“`

  1. return 另一个 promise

  2. return 一个协同的值 (或者 undefined)

  3. throw 一个一起极度throw new Eror(”);

  4. 包装同步与异步代码

Promise.resolve(someValue);

“`

“`

new Promise(function (resolve, reject) {

  1. 抓获同步极度

resolve(someValue);

new Promise(function (resolve, reject) {

});

throw new Error(‘悲剧了,又出 bug 了’);

“`

}).catch(function(err){

写成

console.log(err);

“`

});

Promise.resolve(someValue);

假若是一头代码,可以写成

“`

Promise.reject(new Error(“什么鬼”));

  1. 抓获同步相当
  1. 几个非常捕获,越发精准的捕获

new Promise(function (resolve, reject) {

somePromise.then(function() {

throw new Error(‘悲剧了,又出 bug 了’);

return a.b.c.d();

}).catch(function(err){

}).catch(TypeError, function(e) {

console.log(err);

//If a is defined, will end up here because

});

//it is a type error to reference property of undefined

假定是联名代码,可以写成

}).catch(ReferenceError, function(e) {

Promise.reject(new Error(“什么鬼”));

//Will end up here if a wasn’t defined at all

  1. 三个越发捕获,更加精准的破获

}).catch(function(e) {

somePromise.then(function() {

//Generic catch-the rest, error wasn’t TypeError nor

return a.b.c.d();

//ReferenceError

}).catch(TypeError, function(e) {

});

//If a is defined, will end up here because

  1. 得到八个 Promise 的再次来到值

  2. .then 格局顺序调用

  3. 设定更高层的成效域

  4. spread

  5. finally

//it is a type error to reference property of undefined

此外情状下都会实施的,一般写在 catch 之后

}).catch(ReferenceError, function(e) {

  1. bind

//Will end up here if a wasn’t defined at all

somethingAsync().bind({})

}).catch(function(e) {

.spread(function (aValue, bValue) {

//Generic catch-the rest, error wasn’t TypeError nor

this.aValue = aValue;

//ReferenceError

this.bValue = bValue;

});

return somethingElseAsync(aValue, bValue);

  1. 赢得七个 Promise 的重临值

  2. .then 形式顺序调用

  3. 设定更高层的成效域

  4. spread

  5. finally

})

其余情状下都会举行的,一般写在 catch 之后

.then(function (cValue) {

  1. bind

return this.aValue + this.bValue + cValue;

somethingAsync().bind({})

});

.spread(function (aValue, bValue) {

或者 你也足以如此

this.aValue = aValue;

var scope = {};

this.bValue = bValue;

somethingAsync()

return somethingElseAsync(aValue, bValue);

.spread(function (aValue, bValue) {

})

scope.aValue = aValue;

.then(function (cValue) {

scope.bValue = bValue;

return this.aValue + this.bValue + cValue;

return somethingElseAsync(aValue, bValue);

});

})

如故 你也足以如此

.then(function (cValue) {

var scope = {};

return scope.aValue + scope.bValue + cValue;

somethingAsync()

});

.spread(function (aValue, bValue) {

可是,那有那几个多的分别,

scope.aValue = aValue;

你必须先表明,有浪费资源和内存败露的高危害

scope.bValue = bValue;

不可以用于放在一个表明式的前后文中

return somethingElseAsync(aValue, bValue);

频率更低

})

  1. all。分外用于于处理一个动态大小均匀的 Promise 列表

  2. join。分外适用于处理多个分其他 Promise

.then(function (cValue) {

“`

return scope.aValue + scope.bValue + cValue;

var join = Promise.join;

});

join(getPictures(), getComments(), getTweets(),

不过,那有尤其多的界别,

function(pictures, comments, tweets) {

您不可以不先声明,有浪费资源和内存败露的高危害

console.log(“in total: ” + pictures.length + comments.length +
tweets.length);

不可能用于放在一个表明式的上下文中

});

频率更低

“`

  1. all。十分用于于处理一个动态大小均匀的 Promise 列表

  2. 亚洲必赢官网 ,join。卓殊适用于处理多少个分其余 Promise

  1. props。处理一个 promise 的 map 集合。唯有有一个难倒,所有的实施都截至

“`

“`

var join = Promise.join;

Promise.props({

join(getPictures(), getComments(), getTweets(),

pictures: getPictures(),

function(pictures, comments, tweets) {

comments: getComments(),

console.log(“in total: ” + pictures.length + comments.length +
tweets.length);

tweets: getTweets()

});

}).then(function(result) {

“`

console.log(result.tweets, result.pictures, result.comments);

  1. props。处理一个 promise 的 map 集合。唯有有一个受挫,所有的执行都得了

});

“`

“`

Promise.props({

  1. any 、some、race

pictures: getPictures(),

“`

comments: getComments(),

Promise.some([

tweets: getTweets()

ping(“ns1.example.com”),

}).then(function(result) {

ping(“ns2.example.com”),

console.log(result.tweets, result.pictures, result.comments);

ping(“ns3.example.com”),

});

ping(“ns4.example.com”)

“`

], 2).spread(function(first, second) {

  1. any 、some、race

console.log(first, second);

“`

}).catch(AggregateError, function(err) {

Promise.some([

err.forEach(function(e) {

ping(“ns1.example.com”),

console.error(e.stack);

ping(“ns2.example.com”),

});

ping(“ns3.example.com”),

});;

ping(“ns4.example.com”)

“`

], 2).spread(function(first, second) {

有可能,战败的 promise 相比多,导致,Promsie 永远不会 fulfilled

console.log(first, second);

  1. .map(Function mapper [, Object options])

}).catch(AggregateError, function(err) {

用来拍卖一个数组,或者 promise 数组,

err.forEach(function(e) {

Option: concurrency 并发现

console.error(e.stack);

map(…, {concurrency: 1});

});

以下为不限量并发数量,读书文件新闻

});;

var Promise = require(“bluebird”);

“`

var join = Promise.join;

有可能,战败的 promise 相比较多,导致,Promsie 永远不会 fulfilled

var fs = Promise.promisifyAll(require(“fs”));

  1. .map(Function mapper [, Object options])

var concurrency = parseFloat(process.argv[2] || “Infinity”);

用于拍卖一个数组,或者 promise 数组,

var fileNames = [“file1.json”, “file2.json”];

Option: concurrency 并发现

Promise.map(fileNames, function(fileName) {

map(…, {concurrency: 1});

return fs.readFileAsync(fileName)

以下为不限制并发数量,读书文件新闻

.then(JSON.parse)

var Promise = require(“bluebird”);

.catch(SyntaxError, function(e) {

var join = Promise.join;

e.fileName = fileName;

var fs = Promise.promisifyAll(require(“fs”));

throw e;

var concurrency = parseFloat(process.argv[2] || “Infinity”);

})

var fileNames = [“file1.json”, “file2.json”];

}, {concurrency: concurrency}).then(function(parsedJSONs) {

Promise.map(fileNames, function(fileName) {

console.log(parsedJSONs);

return fs.readFileAsync(fileName)

}).catch(SyntaxError, function(e) {

.then(JSON.parse)

console.log(“Invalid JSON in file ” + e.fileName + “: ” + e.message);

.catch(SyntaxError, function(e) {

});

e.fileName = fileName;

结果

throw e;

$ sync && echo 3 > /proc/sys/vm/drop_caches

})

$ node test.js 1

}, {concurrency: concurrency}).then(function(parsedJSONs) {

reading files 35ms

console.log(parsedJSONs);

$ sync && echo 3 > /proc/sys/vm/drop_caches

}).catch(SyntaxError, function(e) {

$ node test.js Infinity

console.log(“Invalid JSON in file ” + e.fileName + “: ” + e.message);

reading files: 9ms

});

  1. .reduce(Function reducer [, dynamic initialValue]) -> Promise

结果

Promise.reduce([“file1.txt”, “file2.txt”, “file3.txt”],
function(total, fileName) {

$ sync && echo 3 > /proc/sys/vm/drop_caches

return fs.readFileAsync(fileName, “utf8”).then(function(contents) {

$ node test.js 1

return total + parseInt(contents, 10);

reading files 35ms

});

$ sync && echo 3 > /proc/sys/vm/drop_caches

}, 0).then(function(total) {

$ node test.js Infinity

//Total is 30

reading files: 9ms

});

  1. .reduce(Function reducer [, dynamic initialValue]) -> Promise
  1. Time

Promise.reduce([“file1.txt”, “file2.txt”, “file3.txt”],
function(total, fileName) {

.delay(int ms) -> Promise

return fs.readFileAsync(fileName, “utf8”).then(function(contents) {

.timeout(int ms [, String message]) -> Promise

return total + parseInt(contents, 10);

Promise 的实现

});

q

}, 0).then(function(total) {

bluebird

//Total is 30

co

});

when

  1. Time

ASYNC

.delay(int ms) -> Promise

async 函数与 Promise、Generator
函数一样,是用来取代回调函数、解决异步操作的一种格局。它实质上是
Generator 函数的语法糖。async 函数并不属于 ES6,而是被列入了 ES7。

.timeout(int ms [, String message]) -> Promise

参考文献(说是抄也得以的):

Promise 的实现

小编:流星狂飙

q

链接:

bluebird

來源:简书

co

小说权归小编所有。商业转发请联系小编得到授权,非商业转发请阐明出处。

when

ASYNC

async 函数与 Promise、Generator
函数一样,是用来顶替回调函数、解决异步操作的一种办法。它实质上是
Generator 函数的语法糖。async 函数并不属于 ES6,而是被列入了 ES7。

网站地图xml地图