0%

“npm ls –depth 0” 报错 “cb.apply is not a function”
我是安装nvm 切换node版本后报错的

  1. win + r 打开运行,输入%appdata%
  2. 删除 npmnpm-cache 文件夹
  3. 执行npm cache clean --force命令

此时应该就可以了。如果还不行,就执行卸载Node.js重新安装。

时间的4个特性

  • 无法开源
  • 无法节流
  • 无法取代
  • 无法再生

时间管理6原则

时间管理6原则

时间管理6步骤——做正确的事

时间管理6步骤

四象限原则

四象限

每天时间安排(9宫格)

每天时间安排

TensorFlow 的两个依赖

Protocol Buffer

结构化处理工具,比json,xml更小,解析更快

Bazel 自动化构建工具,类似于maven

  • BUILD 文件用于表示构建目标
  • WORKSPACE 文件用于标记外部依赖

安装

三种方式安装: docker pip 源码
我们使用pip来安装

安装 Anaconda

python的发行版,除了python,还包含了很多常用的关于科学计算的库
我参考这篇文章安装成功的 https://www.cnblogs.com/ljysy/p/10660885.html

我这里下载的是 Anaconda 版本是 Anaconda3-5.3.1-Windows-x86_64.exe 对应的 python3.7.0

  1. conda create -n tensorflow python=3.7.0 创建了一个独立的tensorflow的工作空间,在这个空间内使用的python版本为3.7.0
  2. conda activate tensorflow 激活这个名为tensorflow空间
  3. pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple 在空间内配置pip的镜像源
  4. pip install tensorflow

Anaconda + vscode 配置

https://www.jianshu.com/p/ef1ae10ba950

如果提示pip的版本过低,就使用下面的命令升级pip

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U

code-runner 输出乱码

参考这个文章解决 https://www.cnblogs.com/zhaoshizi/p/9050768.html

在项目下 .vscode/settings.json 配置python的路径以及 code-runner的执行路径,设置成全局的也行吧

1
2
3
4
5
6
{
"python.pythonPath": "F:\\ProgramData\\Anaconda3\\envs\\tensorflow",
"code-runner.executorMap": {
"python": "set PYTHONIOENCODING=utf8 && F:\\ProgramData\\Anaconda3\\envs\\tensorflow\\python.exe",
},
}

验证

试下tensorflow官网的例子,能跑起来就是成功了。有时候下载测试数据集的时候会失败,多试几次就好了

工具名称 维护人员 支持语言
Caffe 加州大学伯利克分校视觉与学习中心 C++,py,matlab
Deeplearning4j Skymind java,scala,clojure
Microsoft Cognitive Toolkit( CNTK) 微软研究院 py,c++
MXNext 分布式机器学习社区 py,c++,go,r,…
PaddlePaddle 百度 py,c++
TensorFlow google py,c++
Theano 蒙特利尔大学 py
Torch lua,c, luaJIT
PyTorch py

由于历史原因,项目组维护的项目有 vue angularjs angular 多个包。招聘成本比较高,需要统一技术栈,全部使用vue重构。
这里记录下迁移过程,希望能帮助有需要的人。
本文记录从angularjs 迁移到vue ,后续angular 迁移到vue单独成文

路由 ui-router To vue-router

main.js这部分直接重写

多语言处理 angular-translate To vue-i18n

1. 模板中的 translate

  1. 使用正则替换 translate\s?=\s?["'](.*?)["'] v-t="'$1'" , 替换下面这三种格式成指令
    image.png
  2. 定义全局指令 v-t
    1
    2
    3
    Vue.directive('t', function (el,binding) {
    el.innerText=binding.value
    })
    $如果有用到 filter的话就还要定义filter,我们项目没有这么用就没写 $

    2. js中的 $translate.instant

    在有注入$translate的地方导入 vue-i18n的实例
    1
    2
    import $translate from './i18n/' // 假设/i18n/index.js 默认导出的是vue-i18n的实例
    $translate.instant=$translate.t //instant属性跟 vue-i18n的实例t是一样的

    $scope 与 $http 处理

    首先将模块从这个样子
    1
    2
    module.exports=['$scope',function($scope,$http){
    }]
    改成下面这个样子
    1
    2
    3
    4
    export default function($scope,$http){
    return $scope
    }

    这里我写了一个通用方法getVueOptions处理$scope
    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
    // 这些代码写在一起是为了方便查看学习,项目中肯定会抽成模块
    import axios from axios;//使用axios代替$http
    const instance=axios.create()
    instance.default.header.common['content-type']='application/json'
    // 将$scope分解成vue组件的 methods 与 data
    function getVueOptions($scope) {
    const options = {
    data: {},
    methods: {}
    }
    for (const key in object) {
    if (Object.prototype.hasOwnProperty.call(object, key)) {
    const element = object[key];
    if (element instanceof Function) {
    options.methods[key] = element
    } else {
    options.data[key] = element
    }
    }
    }
    return options
    }
    let scope={}
    const opts=getVueOptions(fn(scope,instance))
    export default {
    data(){
    return {
    ...opts.data
    }
    },
    methods:{
    ...opts.methods
    }
    }

    ng-init 处理

    这个放到 mounted()里面调用this.xxx即可

模板中的替换

把angular组件模板xx.html,直接贴到vue组件的 <template><div></div></template>

搜索 ng- ng-前面有空格,如果是方法的话(ng-cick,ng-mouseover)就换成@ ,属性(ng-class,ng-style)就换成:,指令(ng-if,ng-show,ng-model)就换成v-

属性绑定方式修改

正则模式搜索 \s(.*?)\s?=\s?(["'])\{\{(.*?)\}\}\2 替换成 :$1="$3"
image.png

rootScope 处理

我这项目没怎么用,如果有用到就 export导出下,利用provide注入到根组件,用到的地方 inject一下

其他依赖注入

用类似axios的替换模式一个一个换成你vue的替代品即可,如果使用方式有差异就用正则查询全局替换就好了

剩下的就是体力活儿了

组件 指令 过滤器 一个一个的重构就好了
组件的props有些是双向绑定的,可以使用vue的 xxx.sync this.$emit('update:xxx')的写法来搞定

下一篇 :《 typescript angular7 项目迁移到 vue2.x 》等我把改完再发心得

先阅读下这个文章《使用 MonoRepo 管理前端项目》 https://blog.csdn.net/qiwoo_weekly/article/details/112000852
文章说的是,多npm包管理是每个包对应一个git仓好,还是一个包对应一个仓库好。
先表明我的观点:我支持multi-repo,一个包对应一个仓库

文中列出的multi-repo缺陷

image.png

“如果有脚手架” 难道现在还有不用脚手架的么???所以这是个伪命题,哪有这么多如果

image.png

  1. 硬盘问题:硬盘大的苹果本子太贵了??缺硬盘??10个项目的node_modules大概也就一部电影的大小,这又是个伪命题。
  2. “每次有个新页面” webpack-plugin-html 不能解决?非要去创建新项目?至于说域名部署,shell脚本就可以处理了
  3. “项目分散” vscode可以创建工作空间,工作空间可以添加项目文件夹 Ctrl+R 也是可以快速切换项目的

image.png

这个确实是问题,但是作为包的使用,调用方只关心输入与输出,而不应该关心包内部的实现细节。你在写自己的代码,还要调试被依赖的代码,这本身就是个错误的开发流程。如果A模块依赖B模块的功能,肯定需要B模块自身是稳定模块,如果处于开发中的状态,应该继续完善B模块。

文中提到的前后端放在一个仓库中

如果是小型项目是没问题的,大型项目CI/CD,lint什么的可能完全不一样,也不是很建议,要视情况而定

总结

MonoRepo 管理前端项目,确实有很多场景可以使用。但文中的理由都有点牵强,我认为开发过程中包会越来越多,越来越大。当项目小的时候就适合用MonoRepo 管理多个包,方便省事儿。等仓库达到一定的大小,比如50M的代码量,就要考虑分包,分库,分专人维护。一人—-一包—-一仓库,做到以人为本。。

rust开发环境搭建

rustup-init 下载地址 https://www.rust-lang.org/zh-CN/tools/install

rustup 用于管理rust的版本,类似于管理node版本的nvm

C++ 编译工具链 下载地址 https://visualstudio.microsoft.com/zh-hans/visual-cpp-build-tools/

rust 最终还是会编译成C++ 代码的,只勾选C++工具链即可。安装需要存储空间挺大的,不要装C盘
windows上我们选择 msvc(微软visual-cpp)工具链

rustup-init 安装rust

这里可以使用 清华的rustup-mirror加速安装 RUSTUP_DIST_SERVER=https://mirrors.tuna.tsinghua.edu.cn/rustup
环境变量设置.png

rustup-init是个命令行工具,按照默认的安装设置即可

rustup-init.png

配置cargo镜像

参考这个文章 https://cargo.budshome.com/reference/source-replacement.html
可以配置 CARGO_HOME 环境变量修改cargo的目录,因为默认在 C:\Users\{username}\.cargo,而且后面安装东西也在这么目录,最好还是设置下吧

默认是从国外下载,时间就是生命

确认安装是否成功

rustup --version 能看到正确的版本号 rustc不报错就可以了

安装cargo-edit

cargo install cargo-edit

安装wasm-pack

cargo install wasm-pack

Async Logo

For Async v1.5.x documentation, go HERE

Async 是一个功能强大的异步 JavaScript 模块。虽然最初是为了与 Node.js 一起使用,可以通过 npm i async安装,
它也可以直接在浏览器中使用。

Async 也能通过 yarn 安装:

  • yarn: yarn add async

Async提供了大约70个函数。其中包括集合 (map, reduce, filter, each…) 的异步扩展。以及常见的异步控制流模式 (parallel, series, waterfall…). 这些函数假定您遵循Node.js约定 (提供一个callback作为异步函数的最后一个参数– 一个期望错误作为其第一个参数的回调—并调用回调一次)。

你也可以用 async 函数代替 callback-accepting 函数提供给 Async 方法. 更多信息,请参考 AsyncFunction

Quick Examples

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async.map(['file1','file2','file3'], fs.stat, function(err, results) {
// results is now an array of stats for each file
});

async.filter(['file1','file2','file3'], function(filePath, callback) {
fs.access(filePath, function(err) {
callback(null, !err)
});
}, function(err, results) {
// results now equals an array of the existing files
});

async.parallel([
function(callback) { ... },
function(callback) { ... }
], function(err, results) {
// optional callback
});

async.series([
function(callback) { ... },
function(callback) { ... }
]);

还有更多的功能哦,请查看后面完整功能列表。如果您觉得还缺了点什么,请创建一个GitHub issue 给我们。

常见问题 (StackOverflow)

同步迭代函数 (Synchronous iteration functions)

在使用async的时候,如果你遇到类似 RangeError: Maximum call stack size exceeded.的报错或者其他栈溢出问题 , 你可能使用了同步迭代。使用“同步”,意味着一个方法与它的callback在同一个事件循环(javascript event loop)中执行,使用 I/O 或者 定时器 除外。调用太多的callback会使栈溢出。如果你遇到这个问题,只需要通过async.setImmediate方法启动一个新的调用栈在下一次事件循环中运行。

如果在某些情况下提早回调,这也偶然会产生:

1
2
3
4
5
6
7
8
9
async.eachSeries(hugeArray, function iteratee(item, callback) {
if (inCache(item)) {
callback(null, cache[item]); // 如果太多的项目被缓存,也会栈溢出
} else {
doSomeIO(item, callback);
}
}, function done() {
//...
});

该成这样:

1
2
3
4
5
6
7
8
9
10
async.eachSeries(hugeArray, function iteratee(item, callback) {
if (inCache(item)) {
async.setImmediate(function() {
callback(null, cache[item]);
});
} else {
doSomeIO(item, callback);
//...
}
});

出于性能考虑,Async没有做同步迭代器校验。如果仍然遇到堆栈溢出,可以按照上面的方法进行延迟。或者使用async.ensureAsync方法包装函数,这些函数本质上是异步的,因此不存在此问题,也不需要额外的回调延迟。

如果JavaScript的事件循环仍然有点模糊,请查看this article or this talk 获取更多详细信息。

多次调用回调函数( Multiple callbacks)

确保在调用callback后return这个函数,否则在许多情况下会导致多次回调和不可预知的行为。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
async.waterfall([
function(callback) {
getSomething(options, function (err, result) {
if (err) {
callback(new Error("failed getting something:" + err.message));
// we should return here
}
// since we did not return, this callback still will be called and
// `processData` will be called twice
callback(null, result);
});
},
processData
], done)

每当回调调用不是函数的最后一个语句时,最好使用 return callback(err, result)

Using ES2017 async functions

Async 可以用 async functions 代替 Node-风格 回调函数。没有callback形参,通过return 替代callback(null,result)。通过抛异常throw new Error()代替 callback(err)

1
2
3
4
5
6
7
8
9
10
11
async.mapLimit(files, 10, async file => { // <- no callback!
const text = await util.promisify(fs.readFile)(dir + file, 'utf8')
const body = JSON.parse(text) // <- a parse error here will be caught automatically
if (!(await checkValidity(body))) {
throw new Error(`${file} has invalid contents`) // <- this error will also be caught
}
return body // <- return a value!
}, (err, contents) => {
if (err) throw err
console.log(contents)
})

我们只能识别到原生async functions,不包括被转移的版本(e.g. with Babel)。另外您可以通过async.asyncify()async functions 包装成 node-style callback

绑定上下文 Binding a context to an iteratee

传递给Async的异步函数,this的上下文指向会被改变,需要通过bind方法指定上下文

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
// Here is a simple object with an (unnecessarily roundabout) squaring method
var AsyncSquaringLibrary = {
squareExponent: 2,
square: function(number, callback){
var result = Math.pow(number, this.squareExponent);
setTimeout(function(){
callback(null, result);
}, 200);
}
};

async.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result) {
// result is [NaN, NaN, NaN]
// This fails because the `this.squareExponent` expression in the square
// function is not evaluated in the context of AsyncSquaringLibrary, and is
// therefore undefined.
});

async.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result) {
// result is [1, 4, 9]
// With the help of bind we can attach a context to the iteratee before
// passing it to Async. Now the square function will be executed in its
// 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`
// will be as expected.
});

内存泄漏 Subtle Memory Leaks

在某些情况下,当您在另一个异步函数中调用Async方法时,您可能想尽早退出异步流:

1
2
3
4
5
6
7
8
9
10
11
12
13
function myFunction (args, outerCallback) {
async.waterfall([
//...
function (arg, next) {
if (someImportantCondition()) {
return outerCallback(null)
}
},
function (arg, next) {/*...*/}
], function done (err) {
//...
})
}

有时候你想跳过瀑布流的剩余过程,你调用了外部的回调函数,但是Async还是会等待内部的next函数被调用,从而造成函数没有正确结束。

从 3.0版本, 你可以调用任何 false 作为 error 参数的Async 回调,让Async结束方法。

1
2
3
4
5
6
function (arg, next) {
if (someImportantCondition()) {
outerCallback(null)
return next(false) // ← signal that you called an outer callback
}
},

处理集合时对集合进行改变 Mutating collections while processing them

如果你通过一个数组去调用Async的集合方法(例如 each, mapLimit, or filterSeries),
然后数组被push, pop, or splice 这些方法修改,这可能会导致意外,或者不确定的行为。Async会迭代到满足数组的原始 length次数。一些 push, pop, or splice 的索引已经被处理。因此,不建议在异步开始对其进行迭代之后修改该数组。 如果确实需要push, pop, or splice,请改用 queue

下载


GitHub下载源码.
也可以通过npm安装:

1
$ npm i async

然后 require()引入整个模块:

1
var async = require("async");

或缺部分引入某个方法:

1
2
var waterfall = require("async/waterfall");
var map = require("async/map");

开发版: async.js - 29.6kb 未压缩

在浏览器 In the Browser

Async 可以运行在任何 ES2015 环境 (Node 6+ and all modern browsers).

如果你想在更老的环境中使用Async, (e.g. Node 4, IE11) 你需要做如下转换.

Usage:

1
2
3
4
5
6
7
8
<script type="text/javascript" src="async.js"></script>
<script type="text/javascript">

async.map(data, asyncProcess, function(err, results) {
alert(results);
});

</script>

Async 的可移植版本, 包含 async.js and async.min.js, 在 /dist 文件夹下. Async 支持 jsDelivr CDN.

ES Modules 支持

Async包含一个 .mjs版本,该版本应由兼容的打包工具自动使用,例如Webpack或Rollup等任何使用 package.jsonmodule字段的东西。

我们还在npm上的另一个async-es包中提供Async作为纯ES2015模块的集合。

1
$ npm install async-es
1
2
import waterfall from 'async-es/waterfall';
import async from 'async-es';

Typescript 支持

Async 的第三方类型定义。

1
npm i -D @types/async

建议在您的tsconfig.json中编译选项中配置ES2017或更高版本,这样会保留async函数:

1
2
3
4
5
{
"compilerOptions": {
"target": "es2017"
}
}

其他仓库(友情链接)

  • limiter a package for rate-limiting based on requests per sec/hour.
  • neo-async an altername implementation of Async, focusing on speed.
  • co-async a library inspired by Async for use with co and generator functions.
  • promise-async a version of Async where all the methods are Promisified.

docjs 文档

不是很好翻译,直接看官方英文的
https://caolan.github.io/async/



———华丽的分割线, 后面是我的理解——————



Async 主要包含三个部分的函数

1. 对集合的异步拓展,Array.prototype上方法的补充,原生是不支持异步的

  • concat
  • detect find
  • each forEach –不带index
  • eachOf forEachOf –带index
  • every
  • filter
  • groupBy
  • map
  • mapValues
  • reduce reduceRight transform –都是做累加的
  • reject –filter的补集
  • some
  • sortBy
  • 以上方法又有Limit变体版本
  • Limit是限制并发数量的
  • Series又是Limit并发数为1的变体版本

2. 异步工作流

  • 工作流

    • queue 队列,执行器只能拿到当前执行的任务
    • priorityQueue 跟queue相同,但是任务带执行优先级
    • cargo 队列,执行器中拿到队列中的所有任务信息
    • waterfall 瀑布,从上往下执行,最后的回调拿到瀑布中最后一个函数的执行结果
    • series 一个一个运行,最后的回调拿到所有函数的执行结果
    • auto 自动运行,根据异步函数的依赖关系自动执行
    • times 执行同一个函数多次
    • retry 执行失败会重试,直到成功(默认重试5次,重试间隔0秒)
    • forever 一直执行这个函数,除非报错
  • Promise对应的Async实现

    • parallel 一起执行,任何一个报错就退出,等同于Promise.all [拿到全部正确的结果]
    • Promise.allSettled [拿到全部结果,不论对错],这个Async没有对应的实现,reflect和reflectAll可以阻止错误终端工作流
      1
      async.allSettled=async.parallel(async.reflectAll(tasks))
    • tryEach 任何一个异步函数成功就会退出工作流,等同于Promise.any [拿到第一个正确的结果]
    • race 任何一个异步函数成功或者失败就是退出工作流,等同于Promise.race[拿到第一个执行完成的结果,不论对错]
  • 循环工作流

    • whilst while(condition){statement} 条件满足继续循环
    • doWhilst do{statement}while(condition) 至少会执行一次
    • until loop{}until(condition) 条件满足退出循环
    • doUntil do{}until(condition) 至少会执行一次
  • 辅助函数
    • compose 打散管道方法 例如 a(b(c())) compose(a,b,c)
    • seq compose的可读版本 seq(a,b,c) 对应的是 c(b(a()))
    • applyEach 多个异步方法的参数相同,并发版
    • applyEachSeries 多个异步方法的参数相同,排队版

3. 辅助函数

  • apply 多参数转换成只有一个callback参数的函数
  • asyncify,wrapSync 包装 ES2017 async function 成 Node-style风格AsyncFunction
  • constant 包装常量成 AsyncFunction
  • dir,log 方便调试输出异步函数的执行结果
  • ensureAsync 确保函数是异步执行,防止同步调用迭代栈溢出
  • memoize unmemoize 慢函数增加缓存,清除缓存
  • nextTick 将函数放入下一次事假循环执行
  • reflect reflectAll 返回一个新的函数,及时callback(error)也不会导致工作流结束
  • setImmediate 跟nextTick差不多
  • timeout 给异步函数增加超时

rxjs

rxjs是一个响应式编程的js实现,angular工程里面经常会用到

核心概念

主要是实现了设计模式中的观察者模式
观察者模式主要实现对象间一对多的行为模式。

  • 可观察对象 Observable
  • 观察者 Observer

完整的Observer观察者需要需要3个处理器

  • 消息处理器Next (必选的)
  • 错误处理器Error
  • 观察结束处理器 complete
    1
    2
    3
    4
    5
    const observer = {
    next: x => console.log('Observer got a next value: ' + x),
    error: err => console.error('Observer got an error: ' + err),
    complete: () => console.log('Observer got a complete notification'),
    };
    可观察对象Observable能够向观察者发射这三种类型的消息给观察者
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 这里我简单的模拟下Observable对象的结构
    class Observable{
    constructor(fn){
    this.fn=fn
    }
    subscribe(observer){
    this.fn(observer)
    }
    }
    let observable=new Observable(subscriber => {
    subscriber.next(1);
    //加了这句后面就不执行了,直接走错误处理器
    // throw '报错了'
    subscriber.next(2);
    subscriber.next(3);
    setTimeout(() => {
    subscriber.next(4);
    subscriber.complete();
    }, 1000);
    });
    加上observable.subscribe(observer)这句代码,就可以让观察者执行被观察对象发出的消息了。

实现观察者模式

上面的实现,是一对一的观察(单播)。观察者模式是一对多(多播),跟事件增加处理器一样的道理。rxjs增加了一个中间人Subject做分发。

就像有个山洞,只能进去一个人,但是有一堆人想知道里面的消息。那么派一个人观察,然后告诉大家里面发生了什么就可以了。

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
// 模拟一个 Subject 类做消息分发
class Subject{
this.observerList=[]
subscribe(observer){
observerList.push(observer)
}
unsubscribe(observer){
let index=this.observerList.findIndex(observer)
this.observerList=[]
}
complete(){
this.observerList.forEach(observer => {
observer.complete()
});
}
error(err){
this.observerList.forEach(observer => {
observer.error(err)
});
}
next(arg){
this.observerList.forEach(observer => {
observer.next(arg)
});
}
}
let subject=new Subject() //找个中间人
let observer1=()=>{console.log('观察者1')}
let observer2=()=>{console.log('观察者2')}
subject.subscribe(observer1)//观察者1订阅中间人的消息
subject.subscribe(observer2)//观察者2订阅中间人的消息
observable.subscribe(subject) // 中间人订阅可观察对象

取消订阅

订阅会产生订阅信息subscription,订阅信息可以做从属关系,主订阅信息取消,从订阅信息也会被取消

1
2
3
4
let subscription1=subject.subscribe(observer1)
let subscription2=subject.subscribe(observer2)
subscription1.add(subscription2)//建立从属关系
subscription1.unsubscribe()//这时候subscription2也跟着一起取消了

中间人还有几个变体

  • BehaviorSubject 这个中间人会把你订阅时候前一次消息告知观察者(比如我打开电视去看足球赛,旁边一个老铁跟我说了句,现在中国队0:7落后。我拿到这个消息,可能会立马砸掉电视,而不会继续观看。)
  • ReplaySubject 这个中间人有一个缓冲区,和一个缓冲时间。他会把观察者订阅之前出于缓冲区内且在缓冲时间内的消息,告知观察者。
  • AsyncSubject 这个中间人只会等待complete后通知最后的结果(比如观测者不关心过程,只关心结果的情况)

分离订阅时间点

目前观察者跟可观察对象,是在建立联系的那一刻。可观察对象就产生消息。我们需要控制联系的时间。比如一个在线视频,我需要等前面的广告播放完再看。
rxjs是怎么做的呢?

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
import { interval, Subject } from 'rxjs';
import { multicast } from 'rxjs/operators';

const source = interval(500);
const subject = new Subject();
const multicasted = source.pipe(multicast(subject));
let subscription1, subscription2, subscriptionConnect;

subscription1 = multicasted.subscribe({
next: (v) => console.log(`observerA: ${v}`)
});
// We should call `connect()` here, because the first
// subscriber to `multicasted` is interested in consuming values
subscriptionConnect = multicasted.connect();

setTimeout(() => {
subscription2 = multicasted.subscribe({
next: (v) => console.log(`observerB: ${v}`)
});
}, 600);

setTimeout(() => {
subscription1.unsubscribe();
}, 1200);

// We should unsubscribe the shared Observable execution here,
// because `multicasted` would have no more subscribers after this
setTimeout(() => {
subscription2.unsubscribe();
subscriptionConnect.unsubscribe(); // for the shared Observable execution
}, 2000);

运算符 Operators

我们在上述例子中使用了不少rxjs的运算符。运算符包含两种:

  • 一种是创建运算符,这种运算符的返回值都是一个可观察对象。它能从集合,定时任务,DOM事件创建可观察对象。
  • 另一种是管道运算符,这种运算符用于对可观测对象发出的消息进行处理,比如做消息内容的整合,消息时间的调整。

调度器 Scheduler

调度器分类

  • null 同步传递消息
  • queueScheduler 队列调度器,事件宏任务
  • asapScheduler 微任务调度器,在两次宏任务之间执行,用于promise
  • asyncScheduler 异步调度器,用于定时器
  • animationFrameScheduler 动画调度器

调度器用处

  • 作为静态操作符的参数
  • subscribeOn(scheduler) 在调用subscribe()函数的时候,作为执行的上下文
  • observeOn(scheduler) 当可观察对象执行next()函数的时候,作为执行上下文

    来个例子

    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
    //调度器枚举值,实际上是调度器对象,还能配置,这里简化处理
    const schedulerType={
    asyncScheduler:'asyncScheduler',
    queueScheduler:'queueScheduler',
    asapScheduler:'asapScheduler',
    animationFrameScheduler:'animationFrameScheduler',
    }

    // 模拟个from运算符
    function from(array, scheduler){
    let observe;
    let observe=subscriber => {
    array.forEach(element => {
    subscriber.next(element)
    });
    }
    if(scheduler==schedulerType.asyncScheduler){
    observe=subscriber => {
    setTimeout(() => {
    array.forEach(element => {
    subscriber.next(element)
    });
    }, 0);

    }
    }
    return new Observable(observe);
    }
    console.log('start')
    from([1,2,3]).subscribe(x=>console.log(x))
    console.log('end')
    //logs打印如下
    //start
    //1
    //2
    //3
    //end
    console.log('start')
    from([1,2,3],schedulerType.asyncScheduler).subscribe(x=>console.log(x))
    console.log('end')
    //logs打印如下
    //start
    //end
    //1
    //2
    //3