Promise 从入门到实战

JavaScript 编写异步代码

  • 回调函数
  • 事件监听
  • 发布订阅,消息中心
  • Promise/A+
  • Generator
  • async/await
  • Thunk

Promise 示例

Promise 值具有穿透性,穿透一个没有用的 .then() 直接到达 一个有用的 .then()

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
let p1 = new Promise((resolve,reject) => {
resolve(1)
})

p1.then()
.then(value => {
console.log(value);
return value + 1
}).then(value => {
console.log(value);
})

// output: 1 2

// 两者不等价
let p2 = Promise.resolve(1);
p2.then(v => { v + 1 })
.then(v => { console.log(v)});

// output: 2

let p3 = Promise.resolve(1);
p3.then(v => v + 1);
p3.then(v => console.log(v));

// output: 1

let p4 = Promise.resolve(1);
p4.then(v => {
return new Promise((resolve,reject) => {
setTimeout(() => resolve(2))
})
}).then(v => console.log(v));

// output: 2

let p1 = new Promise((resolve , reject) => {
reject(new Error ('some wrong'))
})

let p2 = p1.then(
value => { console.log(value) },
reason => { console.log(reason.message) }
)

// output: something wrong

p2.then(()=>{
throw new Error('something wrong 1')
}).catch(reason => {
console.log(reason.message)
})

// output: something wrong 1

Promise.reject(new Error('error')))
.catch(reason => {
console.log(reason.message);
return `${reason.message} occured`
}).then(v => console.log(v));

// output: error error occured

function sleep(duration){
return new Promise((resolve,reject) =>{
setTimeout(() => {resolve(),duration})
})
}

async function run(){
const start = Date.now();
await sleep(1000);
await sleep(2000);
await sleep(3000);
console.log(Date.now() - start)
}

run()
// ==== 以下是 promise 的书写方法 === //
function run1(){
const start = Date.now();
return Promise.resolve()
.then(function() { return sleep(1000)})
.then(function() { return sleep(2000)})
.then(function() { return sleep(3000)})
.then(function() { console.log(Date.now() - start)})
}

run1();

实现 Promise 需要注意

  • 符合 Promise/A+ 标准
  • 不暴露过多的函数和变量
  • Promise 状态只能修改一次
  • then() 和 catch 需要异步执行
  • then 能够实现链式调用
  • then 返回新的 Promise
  • then 值能够穿透
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
146
147
148
149
150
151
152
153
154
//  Promise 状态有 3 个  
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
class Promise {
constructor(fn){
// 初始状态
this._state = PENDING;
this._data = null;

this._onFulfilledCallbacks = [];
this._onRejectedCallbakcs = [];

run(this,fn);
}

static deferred (){
const dfd = {}
dfd.promise = new promise ((resonlve,reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}

then(onFulfilled, onRejected){
if(typeof onFulfilled !== 'function'){
onFulfilled = function(data){
return data
}
}
if(typeof onRejected !== 'function'){
onRejected = function(reason){
throw reason
}
}

let promise2;

if(this._state === FULFILLED){
promise2 = new Promise((resolve,reject ) =>{
setTimeout(()=>{
try{
const x = onFulfilled(this._data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e);
}
})
})
}else if(this._state === REJECTED){
promise2 = new Promise((resolve,reject ) =>{
setTimeout(()=>{
try{
const x = onRejected(this._data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e);
}
})
})

}else if(this._state === PENDING){
promise2 = new Promise((resolve,reject)=>{
this._onFulfilledCallbacks.push(data => {
try{
const x = onFulfilled(data)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
this._onRejectedCallbakcs.push(reason =>{
try{
const x = onRejected (reason)
resolvePromise(promise2,x,resolve,reject)
}catch(e){
reject(e)
}
})
})
}
}
return promise2
}

catch(onRejected){
return this.then(undefined,onRejected)
}

function resolvePromise (promise2,x,resolve,reject){
if(x instanceof Promise){
return x.then(resolve,reject);
}else{
resolve(x)
}
}
function run (promise,fn){
try{
fn(
data => {
// do resolve
resolve(promise,data)
},
reason => {
// do reject
reject(promise,reason)
}
)
}catch(e){
// reject
reject(promise,e)
}
}

function resolve(promise,data) {
// 处理值是 Promise 的情况
if(data instanceof Promise){
return data.then(
d => { resolve (promise,d)},
r => { reject (promise,r)}
)
}
if(promise._state !== PENDING){
callback(data)
}

setTimeout(()=>{
promise._state = FULFILLED;
promise._data = data;

for(let callback of promise._onFulfilledCallbacks) {
callback(data)
}
})

}

function reject(promise,reason) {
if(promise._state !== PENDING){
return
}

setTimeout(()=>{
promise_state = REJECTED
promise_data = reason

for (let callback of promise__onRejectedCallbakcs){
callback(reason )
}
})
}

module.exports = Promise;

校验 是否符合 promise 规范

promises-aplus-tests

Promise 是什么

按照用途来解释

  • 主要用于异步计算。
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。
  • 可以在对象之间传递和操作 Promise,帮助我们处理队列。

为什么会有 Promise

JavaScript 包含大量异步操作

JavaScript 为检查表单而生。
创造它的首要目标是操作 DOM。
所以,JavaScript 的操作大多是异步的。
是 UI 语言造成界面冻结是大忌

为什么异步操作可以避免界面冻结

同步问题:按顺序执行,按顺序完成。

异步:完成顺序和执行顺序无关。

异步操作的常见语法

事件监听与响应:

1
2
3
4
5
6
7
8
  document.getElementById("start").addEventListener("click", start, false);

function start() {
// 响应事件,进行相应的操作
}

// jQuery 用 '.on()' 也是事件监听
$("#start").on("click", start);

异步操作的常见语法

1
2
3
4
5
6
7
8
9
10
$.ajax("http://baidu.com", {
success: function (res) {
// 这里就是回到函数了
},
});

// 或者在页面加载完毕后回调
$(function () {
// 这里也是回调函数
});

浏览器中的 JavaScript

  • 异步操作以事件为主
  • 回调主要出现在 Ajax 和 File API

Node.js 的出现

对异步的依赖进一步加剧了

  • 无阻赛高并发,是 Node.js 的招牌。
  • 异步操作是其保障。
  • 大量操作依赖回调函数。

异步回调的问题

  • 嵌套层次很深,难以维护
  • 无法正常使用 returnthrow
  • 无法正常检索堆栈信息
  • 多个回调之间难以建立联系

Promise 详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise(
/* 执行器 executor */
function (resolve, reject) {
// 一段耗时很长的异步操作

resolve(); // 数据处理完成

reject(); // 数据处理出 错
}
).then(
function A() {
// 成功,下一步
},
function B() {
// 失败,做相应处理
}
);

Promise 是一个代理对象,它和原先要进行的操作并无关系。
它通过引入一个回调,避免更多的回调。

Promise 有 3 个状态

pending [待定] 初始状态
fulfilled [实现] 操作成功
reject [被否定] 操作失败

Promise 状态发生改变,就会触发 .then() 里的响应函数处理后续步骤。
Promise 状态一经改变,不会再变。

Promise 执行顺序

范例

1
2
3
4
5
6
7
new Promise((resolve) => {
setTimeout(() => {
resolve("hello");
}, 2000);
}).then((value) => {
console.log(value + "world");
});

两步执行范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 两步执行范例
new Promise( resolve =>{
setTimeout(()=>{
resolve('hello');
},2000);
}).then( value => {
console.log(value);
return new Promise( resolve =>{
setTimeout(() => {
resolve('world');
},2000);
});
}).then( value =>{
console.log( value + 'world');
})

对已完成的 promise 执行

1
2
3
4
5
6
7
8
9
10
11
12
13
// 对已完成的 promise 执行
let promise = new Promise(resolve =>{
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello,world');
},1000)
})

setTimeout(()=>{
promise.then( value =>{
console.log(value);
})
},3000)

then 里不返回 promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// then 里不返回 promise
new Promise( resolve =>{
setTimeout(() =>{
resolve('hello');
},2000);
}).then(value =>{
console.log(value);
console.log('everyone');
(function (){
return new Promise(resolve => {
setTimeout(()=>{
console.log('Mr.Laurence');
resolve(' Merry Xmas ');
},2000)
});
}());
return false;
}).then( value =>{
console.log(value + ' world');
})

.then()

  • .then() 接受两个函数作为参数,分别代表 fulfilled 和 rejected
  • .then() 返回一个新的 Promise 实例,所以它可以链式调用
  • 当前面的 Promise 状态改变时,.then() 根据其最终状态,选择特定的状态响应函数执行
  • 状态响应函数可以返回新的 Promise,或其他值
  • 如果返回新的 Promise,那么下一级 .then() 会在新 Promise 状态改变之后执行。
  • 如果返回其他任何值,则会立刻执行下一级 .then()

.then()里有 .then()的情况

  • 因为 .then() 返回的还是 Promise 实例。
  • 会等里面的 .then() 执行完,再执行外面的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
console.log('start')
new Promise( resolve =>{
console.log('Step 1');
setTimeout(() =>{
resolve('1000');
},1000);
}).then(value =>{
return new Promise( resolve =>{
console.log('Step 1-1');
setTimeout(()=>{
resolve(110);
},1000)
}).then( value => {
console.log('Step 1-2');
return value;
}).then(value => {
console.log('Step 1-3');
return value;
})
}).then( value =>{
console.log(value);
console.log('Step 2');
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 展开
console.log('start')
new Promise( resolve =>{
console.log('Step 1');
setTimeout(() =>{
resolve('1000');
},1000);
}).then(value =>{
return new Promise( resolve =>{
console.log('Step 1-1');
setTimeout(()=>{
resolve(110);
},1000)
})
}).then( value => {
console.log('Step 1-2');
return value;
}).then(value => {
console.log('Step 1-3');
return value;
}).then( value =>{
console.log(value);
console.log('Step 2');
})

下面 Promise 的区别

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
doSomething().then(function(){
return doSomethingElse()
});


// doSomething().then(function(){
// return doSomethingElse();
// }).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|


doSomething().then(function(){
doSomethingElse()
});

// doSomething().then(function(){
// doSomethingElse();
// }).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|

doSomething().then(
doSomethingElse()
);

// doSomething().then(
// doSomethingElse();
// ).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(undefined)
// |----------------------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|


doSomething().then(
return doSomethingElse
);

// doSomething().then(
// doSomethingElse;
// ).then(finalHandler);

// 答案
// doSomething
// |-----------|
// doSomethingElse(resultOfDosomething)
// |------------|
// finalHandler(resultOfDoSomethingElse)
// |------------|

错误处理

Promise 会自动捕获内部异常,并交给 rejected 响应函数处理。
错误处理两种做法:

  • reject(‘错误信息’).then(null,message =>{})
  • throw new Error(‘错误信息’).catch( message => {})

推荐第二种,更加清晰好读,并且可以捕获前面的错误。

1
2
3
4
5
6
7
8
9
new Promise( resolve =>{
setTimeout(()=>{
throw new Error('bye');
},2000);
}).then( value =>{
console.log(value + 'world');
}).catch( error =>{
console.log( 'Error:',error.message )
})
1
2
3
4
5
6
7
8
9
new Promise( (resolve,reject) =>{
setTimeout(()=>{
reject('bye');
},2000);
}).then( value =>{
console.log(value + 'world');
},value =>{
console.log('Error',value);
})

.catch() + .then()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise(resolve =>{
setTimeout(()=>{
resolve();
},1000)
}).then(()=>{
console.log('start');
throw new Error('test error');
}).catch( err => {
console.log('I catch ',err);
// throw new Error('another error');
}).then(()=>{
console.log('arrive here');
}).then(()=>{
console.log('...and here');
}).catch( err =>{
console.log('No,I catch',err);
})
注意: 强烈建议在所有队列最后都加上 .catch(),以避免漏掉错误处理造成意想不到的问题。
1
2
3
4
5
6
doSomething()
.doAnotherThing()
.doMoreThing()
.catch( err =>{
console.log(err);
})

Promise 常用函数

Promise.all()

批量执行

  • Promise.all([p1,p2,p3,…]) 用于将多个 Promise 实例,包装成一个新的 Promise 实例
  • 返回的实例就是普通 Promise
  • 接收一个数组作为参数。
  • 数组里可以是 Promise 对象,也可以是别的值,只有 Promise 会等待状态改变。
  • 当所有子 Promise 都完成,该 Promise 完成,返回值是全部值的数组
  • 有任何一个失败,该 Promise 失败,返回值是第一个失败的子 Promise 的结果。
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
Promise.all([1,2,3]).then( all => {
console.log('1: ',all);
return Promise.all([function(){
console.log('ooxx');
},'xxoo',false]);
}).then(all =>{
console.log('2: ',all);
let p1 = new Promise( resolve =>{
setTimeout(()=>{
resolve('I\'m P1');
},1500)
})
let p2 = new Promise( resolve =>{
setTimeout(()=>{
resolve('I\'m P2');
},1450)
})
return Promise.all([p1,p2]);
}).then( all =>{
console.log('3: ',all);
let p1 = new Promise( resolve =>{
setTimeout(() => {
resolve('I\'m P1');
},1500)
})
let p2 = new Promise( (resolve,reject) =>{
setTimeout(() => {
reject('I\'m P2');
},1000)
})
let p3 = new Promise( (resolve,reject) =>{
setTimeout(() => {
reject('I\'m P3');
},3000)
})
}).then( all =>{
console.log('all',all);
}).catch(err =>{
console.log(err);
})

Promise.add() 和 .map() 连用

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
const fs = require('fs');
const path = require('path');
const FileSystem = require('./FileSystem');

function findLargest(dir) {
return FileSystem.readDir(dir, 'utf-8').then( files => {
return Promise.all( files.map( file => {
return new Promise (resolve => {
fs.stat(path.join(dir, file), (err, stat) => {
if (err) throw err;
if (stat.isDirectory()) {
return resolve({
size: 0
});
}
stat.file = file;
resolve(stat);
});
});
}));
}).then( stats => {
let biggest = stats.reduce( (memo, stat) => {
if(memo.size < stat.size) {
return stat;
}
return memo;
});
return biggest.file;
})
}

实现队列

使用 .forEach()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function queue(things){
let promise = Promise.resolve();
things.forEach( thing =>{
promise = promise.then(()=>{
return new Promise( resolve =>{
doThing(thing,()=>{
resolve();
});
})
})
})
return promise;
}
queue('lots','of','things',...)
常见错误: 没有把 .then()产生的新 Promise 实例赋给 promise,没有生成队列。

使用 .reduce()

1
2
3
4
5
6
7
8
9
10
11
12
function queue(things){
return things.reduce((promise,thing) => {
return promise.then(() => {
return new Promise( resolve => {
doThing(thing,()=>{
resolve();
});
});
});
},Promise.resolve());
}
queue('lots','of','things',...)
常见错误: Promise 实例创建之后,会立刻运行执行器代码,所以这个也无法达成队列的效果。

半成品爬虫

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let url = ['http://blog.meathill.com/'];
function fetchAll(urls) {
return urls.reduce((promise, url) => {
return promise.then( () => {
return fetch(url);
});
}, Promise.resolve());
}
function fetch(url) {
return spider.fetch(url)
.then( content => {
return saveOrOther(content);
})
.then( content => {
let links = spider.findLinks(content);
return fetchAll(links);
});
}
fetchAll(url);

Promise.resolve()

返回一个 fulfilled 的 Promise 实例,或原始 Promise 实例。

  • 参数为空,返回一个状态为 fulfilled 的 Promise 实例。
  • 参数是一个跟 Promise 无关的值,同上,不过 fulfilled 响应函数会得到这个参数
  • 参数为 Promise 实例,则返回该实例,不做任何修改
  • 参数为 thenable,立刻执行它的 .then()。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.resolve().then( (value) => {
console.log('Step 1', value);
return Promise.resolve('Hello');
}).then( value =>{
console.log(value,'World');
return Promise.resolve(new Promise( resolve => {
setTimeout(()=>{
resolve('Good');
},2000);
}));
}).then( value => {
console.log(value, 'evening');
return Promise.resolve({
then(){
console.log(', everyone');
}
})
})

Promise.reject()

返回一个 rejected 的 Promise 实例。

Promise.reject() 不认 thenable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let promise = Promise.reject('something wrong');

promise.then( () => {
console.log('it\'s ok');
}).catch( () => {
console.log('no, it\'s not ok');
return Promise.reject({
then() {
console.log('it will be ok');
},
catch() {
console.log('not yet');
}
});
});

Promise.race()

类似 Promise.all(),区别在于它有任意一个完成就算完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let p1 = new Promise(resolve => {
// 这是一个长时间的调用
setTimeout(() => {
resolve('I\'m P1');
}, 10000);
});

let p2 = new Promise(resolve => {
// 这是个稍短的调用
setTimeout(() => {
resolve('I\'m P2');
}, 2000)
});

Promise.race([p1, p2]).then(value => {
console.log(value);
});

常见用法:

  • 把异步操作和定时器放在一起
  • 如果定时器先触发,就认为超时,告知用户

实际开发的 Promise

异步回调包装成 Promise

优点:

  • 可读性更好
  • 返回的结果可以加入任何 Promise 队列
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
const fs = require('fs');

module.exports = {
// 读取目录
readDir: function (path, options) {
return new Promise( resolve => {
fs.readdir(path, options, (err, files) => {
if (err) {
throw err;
}
resolve(files);
});
});
},
// 读取文件
readFile: function (path, options) {
return new Promise( resolve => {
fs.readFile(path, options, (err, content) => {
if (err) {
throw err;
}
resolve(content);
});
});
}
};

// 使用
const fs = require('./FileSystem');

fs.readFile('../README.md','utf-8').then(content =>{
console.log(content);
})

把任意异步操作包装成 Promise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 弹出窗体
let confirm = popupManager.confirm('您确定么?');
confirm.promise.then(() => {
// do confirm staff
})
.catch(() => {
// do cancel staff
});

// 窗体的构造函数
class Confirm {
constructor() {
this.promise = new Promise( (resolve, reject) => {
this.confirmButton.onClick = resolve;
this.cancelButton.onClick = reject;
})
}
}

两步执行

1
2
3
4
5
6
7
8
9
10
11
12
13
// 对已完成的 promise 执行
let promise = new Promise(resolve =>{
setTimeout(() => {
console.log('the promise fulfilled');
resolve('hello,world');
},1000)
})

setTimeout(()=>{
promise.then( value =>{
console.log(value);
})
},3000)

实际开发会对请求进行 promise 化,两种情况

返回的数据直接更改值

1
2
3
4
5
6
7
8
9
10
getlist(data){
this.api.getlist(data).then(res=>{
this.list = res.data.data;
})
}

// 下拉刷新
pullDown(){
this.getlist(data);
}

返回数据传递给下个调用的函数

1
2
3
4
5
6
7
8
9
10
11
12
getlist(data){
return this.api.getlist(data).then(res=>{
return Promise.resolve(res.data.data);
})
}

// 模糊搜索
querySearchAsync(){
this.getlist(data).then((value)=>{
this.searchlist = value;
})
}

jQuery

jQuery 已经实现了 Promise。1.5 版开始尝试, 3.0版完成的 Promise。

参考 jQuery 的 ajax 实现。

1
2
3
4
5
$.ajax(url,{
dataType: 'json'
}).then(json =>{
// 进行操作
})

IE

如果需要在 IE 中使用 Promise,有两个选择:

Fetch API

Fetch API 是 XMLHttpRequest 的现代化替代方案。
可以在 service workers、Cache API 使用。

  • 更强大,更友好。
  • 直接返回一个 Promise 实例。
  • response 是一个二进制数据流,需要调用 json() 方法可以转换成 json 数据
1
2
3
4
5
6
7
fetch('some.json').then( res =>{
return res.json()
}).then( json =>{
// do something with the json
}).catch( err =>{
console.log(err);
})

async/await

ES2017 新增运算符,新的语言元素

  • 赋予 JavaScript 以顺序手法编写异步脚本的能力
  • 既保留异步运算的无阻塞特性,还继续使用同步写法。
  • 还能正常使用 return/try/catch。

async/await 仍然需要 Promise!

希望全面了解,请参考 async/await 入门

1
2
3
4
async function f1(){
const result = await resolveAfter2Seconds();
console.log(result);
}

协程

  1. 协程A开始执行。
  2. 协程A执行到一半,进入暂停,执行权转移到协程B。
  3. (一段时间后)协程B交还执行权。
  4. 协程A恢复执行。

Thunk

参数求值

传名调用,在执行时求值

1
2
3
4
5
6
7
8
9
10
11
12
13
function f(m){
return m * 2;
}

f(x + 5);
// 等同于
var thunk = function () {
return x + 5;
};

function f(thunk){
return thunk() * 2;
}

在 JavaScript 语言中,Thunk 函数替换的不是表达式,而是多参数函数,将其替换成单参数的版本,且只接受回调函数作为参数。

1
2
3
4
5
6
7
8
9
10
11
12
// 正常版本的readFile(多参数版本)
fs.readFile(fileName, callback);

// Thunk版本的readFile(单参数版本)
var readFileThunk = Thunk(fileName);
readFileThunk(callback);

var Thunk = function (fileName){
return function (callback){
return fs.readFile(fileName, callback);
};
};

任何函数,只要参数有回调函数,就能写成 Thunk 函数的形式。下面是一个简单的 Thunk 函数转换器。

1
2
3
4
5
6
7
8
9
10
11
12
13
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};

// 使用上面的转换器,生成 fs.readFile 的 Thunk 函数。
var readFileThunk = Thunk(fs.readFile);
readFileThunk(fileA)(callback);

Thunkify 模块

生产环境的转换器,建议使用 Thunkify 模块

Thunk 函数的自动流程管理

控制执行权

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
var fs = require('fs');
var thunkify = require('thunkify');
var readFile = thunkify(fs.readFile);

var gen = function* (){
var r1 = yield readFile('/etc/fstab');
console.log(r1.toString());
var r2 = yield readFile('/etc/shells');
console.log(r2.toString());
};


var g = gen();
var r1 = g.next();
r1.value(function(err, data){
if (err) throw err;
var r2 = g.next(data);
r2.value(function(err, data){
if (err) throw err;
g.next(data);
});
});


function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}
next();
}
run(gen);

Generator

Generator 函数是协程在 ES6 的实现,最大特点就是可以交出函数的执行权(即暂停执行)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* gen(x){  // * 号区别
try {
var y = yield x + 2; // yield 暂停
} catch (e){
console.log(e);
}
return y;
}

var g = gen(1); // 返回的是指针对象
g.next() // { value: 3, done: false } value 属性是 yield 语句后面表达式的值
g.next() // { value: undefined, done: true } Generator 是否执行完毕
// 有参数2,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量 y 接收。
g.next(2) // { value: 2, done: true }
g.throw('出错了'// 捕获错误并抛出

相关资料
Promise 入门
Generator 函数的含义与用法
Thunk 函数的含义和用法
fetch polyfill兼容库
彻底搞懂JS事件中的循环机制 Event Loop
巨大的提升!重启的async函数和promises“译”

async/await的基础用法

Promise 初入门径
Promise 探讨
jQuery的deferred对象详解
Promises/A+ 规范


相关代码


开源
es6-promise

秒杀活动

秒杀活动特点

“限时限量”、“价格极地”、“几秒内就被抢光”

秒杀活动带来的问题:
1、对现有网站业务的冲击
2、高并发情况以及数据库负载
3、突然增加的网路和服务器宽带
4、直接下单
5、防止机器秒杀

解决方案
1、秒杀系统单独部署,如果有需要甚至是独立的域名,根网站完全隔离开,除了底层数据库中某些数据,也就是从 Web 服务器、数据库服务器等都会有单独的一套。
2、在没有开始秒杀之前,把商品的展示页面做静态化,也就是简单的 HTML 页面不做任何业务逻辑的处理,也不访问数据库,就只是很简单的展示,等真正需要秒杀的时候再打开另外一个页面,这个页面有业务逻辑,也会有可能访问数据库的。在秒杀活动之前大多数用户会不停的刷新页面。
3、可以根运营商购买或者是租借网络服务器带宽,将秒杀商品的静态页面缓存到 CDN,租借 CDN。
4、避免让用户在秒杀活动前知道秒杀活动下单的接口或者页面。对下单页面添加动态验证参数,并且该页面是在秒杀开始前无法访问到的。
5、ip 的阻止,一个 ip 可以访问几次,验证码。

windows server 2012 standard 搭建 git 服务 探索

服务器系统介绍

windows server 2012 standard

搭建项目托管平台

在搭建项目托管平台之前我们应该先搞明白版本控制这个概念。

版本控制

什么是 版本控制?

来源 百度百科
版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。
版本控制包括:检入检出控制、分支和合并、历史记录。

来源 TOWER
你可以把一个版本控制系统(缩写VCS)理解为一个“数据库”,在需要的时候,它可以帮你完整地保存一个项目的快照。当你需要查看一个之前的快照(称为“版本” )时,版本控制系统可以显示出当前版本与上一个版本之间的所有替换的细节。

来源 git
版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

为什么用 版本控制?

1、备份用
2、协调开发用

版本控制的变革

来源 git
本地版本控制系统
本地版本控制系统
缺点:
1、中央服务器故障,将无法提交更新,无法协同工作。如果中心数据库所在的磁盘发生损坏,又恰当无备份,,毫无疑问将丢失所有数据
包括项目的整个变更历史。
2、多人协作繁琐。

集中化的版本控制系统
集中化的版本控制系统
缺点:中央服务器故障,将无法提交更新,无法协同工作。如果中心数据库所在的磁盘发生损坏,又恰当无备份,,毫无疑问将丢失所有数据
包括项目的整个变更历史。
记住这个概念下面会有涉及到。

分布式版本控制系统
分布式版本控制系统
解决了本地版本控制系统 集中化的版本控制系统 的缺点。
解决方式:客户端并不只提取最新版本的文件快照, 而是把代码仓库完整地镜像下来,包括完整的历史记录。
记住这个概念下面会有涉及到。

简单的来说版本控制系统会记录所有对项目文件的更改。

版本控制工具

现在主流版本控制工具是 Git 和 SVN。
咱们上面提到两个概念分别是 集中化的版本控制系统 和 分布式版本控制系统。

SVN 属于 集中化的版本控制系统。
Git 属于 分布式版本控制系统。

SVN VS Git

SVN 简介

来源SVN
Apache® Subversion®
“Enterprise-class centralized version control for the masses”

google 翻译
Apache Subversion
面向群众的企业级集中版本控制

提取关键字:企业级、集中、版本控制。
SVN 是一款集中式版本控制系统。
特点:可靠性高、模型和用法的简单性、覆盖面广(支持从个人到大型企业运营的各种用户和项目需求的能力)。

SVN 的一些概念

  • repository(源代码库)
  • Checkout(提取)
  • Commit(提交)
  • Update (更新)

SVN 的主要功能

  • 1、目录版本控制
  • 2、真实的版本历史
  • 3、自动提交
  • 4、纳入版本控管的元数据
  • 5、选择不同的网络层
  • 6、一致的数据处理方式
  • 7、有效的分支(branch)与标签(tag)
  • 8、Hackability

SVN 生命周期

1、创建版本库
2、检出
3、更新
4、执行变更
5、复查变化
6、修复错误
7、解决冲突
8、提交更改

SVN 客户端工具

TortoiseSVN 官网上下载 TortoiseSVN,TortoiseSVN有对应的语言包。
菜鸟教程TortoiseSVN 使用教程

1、新建一个工作目录例如 runoob01,用于存储工作档案

新建工作目录

2、将工作档案更新到本地

SVN checkout
检出至目录
检出至目录
检出至目录
检出至目录

3、新增档案及目录到 Repository 中

新增文件
加入文件
加入文件
加入文件
加入文件
添加到Repository

Git 简介

来源 Git
Git 是一个免费的开源分布式版本控制系统,旨在快速高效地处理从小型到大型项目的所有内容。
Git 易于学习, 占地面积小,具有闪电般的快速性能。它具有Subversion,CVS,Perforce和ClearCase之类的SCM工具,具有廉价的本地分
支,方便的暂存区域和 多个工作流等功能。

Git SVN 能做的 Git 基本都能实现。

Git 的一些概念

本来要搭建 SVN 我提议搭建 GitLab ,我但是我也没有搭建过 GitLab,服务器系统是 windows server,我查了查资料,得到结果是 如果要在 windows server 上搭建 GitLab 就要装 虚拟机 然后装 Ubuntu 之类的 操作系统然后再装 GitLab 那可是真………… ,gitlab官网也没有资料。

查找资料的过程中我发现 GitLab Runner 可以安装在 windows 上,查了下资料大致意思是 GitLab Runner 是将代码发送到 GitLab 上的服务。??…………

GitLab Runner 官方解释
GitLab Runner是一个开源项目,用于运行您的作业并将结果发送回GitLab。它与GitLab CI / CD结合使用,GitLab CI / CD是GitLab随附的用于协调作业的开源持续集成服务。

查找资料的过程中 我发现了 Gitblit 而且相关的搭建文章有很多,我开始了新的尝试。

Git 与 SVN 区别

1、Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 SVN,CVS 等,最核心的区别。
2、Git 把内容按元数据方式存储,而 SVN 是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。
3、Git 分支和 SVN 的分支不同:分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
4、Git 没有一个全局的版本号,而 SVN 有:目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
5、Git 的内容完整性要优于 SVN:Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。

托管平台

  • GitHub
  • GitLab
  • Bitbucket
  • 码云
  • coding

相关资料
版本控制
版本控制
版本控制
程序员编程开发网站,为什么要有版本控制工具这种东西?
Apache® Subversion®
菜鸟教程 SVN
TortoiseSVN
Git
Git工作流程
Git教程
菜鸟教程 Git
Git、Github、Gitlab的区别
Git、GitHub、GitLab三者之间的联系以及区别
gitLab的使用 和 git 、 github、gitlab的区别
Git、GitHub和GitLab的区别
Git与GitHub,Gitlab的区别
如何在你自己的服务器搭建类似github的服务,git部署站点
Git:本地部署代码到服务器(GitHub服务器 或 局域网搭建的git服务器)
如何搭建一个GitHub在自己的服务器上?
Svn与Git的区别,为什么使用git?
SVN与Git比较
版本管理工具介绍——SVN篇
SVN常见问题
版本控制工具 - git和svn
SVN与Git比较的优缺点差异
对比Git 与 SVN,这篇讲的很易懂
你还在用svn?git你无法拒绝的版本管理工具
Bonobo Git Server官网
Bonobo Git Server安装
Bonobo Git Server 先决条件
windows系统部署Bonobo.git服务器
在Windows上搭建Git Server
5分钟搞定 Git 服务端 安装 windows 2003 gitblit
gitblit搭建git服务器
Windows平台下Git服务器搭建
Windows平台下Git(gitblit)服务器搭建
怎么看电脑是否支持使用Hyper-V虚拟机
gitlab
gitblit
搭建gitlab仓库
在Windows上安装GitLab Runner
GitLab Runner
Gitblit GO安装和设置
windows下使用docker搭建gitlab的报错处理
使用docker-compose安装GitLab
安装Docker Compose
安装Docker引擎
服务器上的Git(Windows Server 2012)
Windows下使用docker部署gitlab CI服务
在Windows Server 2012上安装IIS 8
Internet信息服务(IIS)管理器在哪里打开
windows Server2012 IIS8.0配置安装完整教程
在Windows下搭建Gitlab服务器
windows server 2012 r2 安装docker

篇幅过长,回头填坑。

MUI 视频教程

MUI 介绍、新项目创建、基础布局

MUI 为何诞生

1、性能和体验的差距,一直是 mobile app 开发者放弃 HTML5 的首要原因。
2、浏览器天生的切页白屏、不忍直视的转页动画、浮动元素的抖动、无法流畅下拉刷新、侧滑抽屉卡顿等问题,这些都让 HTML5 开发者倍感挫败,尤其在 Android 低端机运行。
3、浏览器默认控件样式又少又丑,制作一个漂亮的控件非常麻烦,也有一些制作简单的 ui 框架但性能低下。mui 框架有效的解决了这些问题,这是一个可以方便开发出高性能 App 的框架,也是目前最接近原生 App 效果的框架。

MUI 的定位是:最接近原生体验的移动 App 的 UI 框架

基于 mui 的定位,产生了 mui 的几个特点,轻、小、只涉及 UI、只为移动 App 而生、界面风格原生话。

1、mui 不是 jq,不封装 dom 操作

与 ui 无关的 mui 不做,可以用 jq 或 zepto 就用,并不冲突。
但并不建议在 移动 App 里引入 jq 或 zepto 这些框架,原因如下:
为了性能,层层封装的框架,尤其是遍历 dom 时,影响效率,尤其在低端 Android 手机上。

原生 JS 挺简单,为何需要 jq ?

手机上只有 webkit 浏览器(忽略 wp,反正 mui 不支持 wp),根本就不需要 jq 这种封装来操作 dom。
而且 HBuilder 提供了代码块来简化开发,敲 dg、dq,直接生成 document.getElementById(“”)、document.querySelectorAll(“”),非常快捷方便,而且执行性能非常高,而且没有浏览器兼容问题。

2、mui、HTML5+、5+ Runtime 的关系说明

mui 是一个前端框架,HTML5+ 是一套 HTML5 能力扩展规范,HTML5+ Runtime 是实现 HTML5+ 规范的强化浏览器引擎。

开始体验 MUI

1、安装新版本 HBuilder

HBuilder

2、新建 app 项目

创建 MUI 项目

基础布局

头部

核心 css mui-bar mui-bar-nav

1
2
3
4
<header class="mui-bar mui-bar-nav">
<a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>
<h1 class="mui-title">test</h1>
</header>

主体部分

核心 css mui-content

1
2
3
<div class="mui-content">
test...
</div>

代码效果

组件

折叠面板

1
2
3
4
5
6
7
8
<ul class="mui-table-view">
<li class="mui-table-view-cell mui-collapse">
<a class="mui-navigate-right" href="#">面板1</a>
<div class="mui-collapse-content">
<p>面板1子内容</p>
</div>
</li>
</ul>

代码效果

常用按钮

1
2
3
4
5
6
<button type="button" class="mui-btn">默认</button>
<button type="button" class="mui-btn mui-btn-primary">蓝色</button>
<button type="button" class="mui-btn mui-btn-success">绿色</button>
<button type="button" class="mui-btn mui-btn-warning">黄色</button>
<button type="button" class="mui-btn mui-btn-danger">红色</button>
<button type="button" class="mui-btn mui-btn-royal">紫色</button>

代码效果

操作表

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
<div class="mui-content">
<button type="button" class="mui-btn" onclick="handleClick()">操作表</button>
</div>
<div id="sheet1" class="mui-popover mui-popover-bottom mui-popover-action ">
<!-- 可选择菜单 -->
<ul class="mui-table-view">
<li class="mui-table-view-cell">
<a href="#">菜单1</a>
</li>
<li class="mui-table-view-cell">
<a href="#">菜单2</a>
</li>
</ul>
<!-- 取消菜单 -->
<ul class="mui-table-view">
<li class="mui-table-view-cell">
<a href="#sheet1"><b>取消</b></a>
</li>
</ul>
</div>
<script>
mui.init();
mui.plusReady(function () {});
function handleClick() {
mui("#sheet1").popover("toggle");
}
</script>

代码效果

数字角标

1
2
3
4
5
6
7
8
9
10
11
12
<span class="mui-badge">1</span>
<span class="mui-badge mui-badge-primary">12</span>
<span class="mui-badge mui-badge-success">123</span>
<span class="mui-badge mui-badge-warning">3</span>
<span class="mui-badge mui-badge-danger">45</span>
<span class="mui-badge mui-badge-purple">456</span>
<span class="mui-badge mui-badge-inverted">1</span>
<span class="mui-badge mui-badge-primary mui-badge-inverted">2</span>
<span class="mui-badge mui-badge-success mui-badge-inverted">3</span>
<span class="mui-badge mui-badge-warning mui-badge-inverted">4</span>
<span class="mui-badge mui-badge-danger mui-badge-inverted">5</span>
<span class="mui-badge mui-badge-royal mui-badge-inverted">6</span>

代码效果

单选框

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
<div class="mui-content">
<div class="mui-input-row mui-radio">
<label>radio</label>
<input name="radio2" type="radio" />
</div>
<div class="mui-input-row mui-radio">
<label>radio</label>
<input name="radio2" type="radio" />
</div>
<div class="mui-input-row mui-radio mui-left">
<label>面条</label>
<input class="radio1" name="radio1" type="radio" value="面条" />
</div>
<div class="mui-input-row mui-radio mui-left">
<label>包子</label>
<input
class="radio1"
name="radio1"
type="radio"
value="包子"
checked="checked"
/>
</div>
<ul class="mui-table-view mui-table-view-radio">
<li class="mui-table-view-cell">
<a class="mui-navigate-right">Item 1</a>
</li>
<li class="mui-table-view-cell mui-selected">
<a class="mui-navigate-right">Item 2</a>
</li>
<li class="mui-table-view-cell">
<a class="mui-navigate-right">Item 3</a>
</li>
</ul>
<button type="button" class="mui-btn" onclick="getVals()">获取值</button>
</div>
<script>
mui.init();
mui.plusReady(function () {});
function getVals() {
let res = getRadioRes("radio1");
if (res == null) {
mui.toast("请选择");
return;
}
mui.toast(res, { duration: "long", type: "div" });
}
function getRadioRes(className) {
let rdsObj = document.getElementsByClassName(className);
let checkVal = null;
for (let i = 0; i < rdsObj.length; i++) {
if (rdsObj[i].checked) {
checkVal = rdsObj[i].value;
}
}
return checkVal;
}
</script>

代码效果

复选框

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
<div class="mui-content">
<div class="mui-input-row mui-checkbox mui-left">
<label>checkbox左侧显示示例</label>
<input name="checkbox1" class="checkbox1" value="Item 1" type="checkbox" />
</div>
<div class="mui-input-row mui-checkbox mui-left">
<label>checkbox左侧显示示例</label>
<input name="checkbox1" class="checkbox1" value="Item 2" type="checkbox" />
</div>
<div class="mui-input-row mui-checkbox mui-left">
<label>checkbox左侧显示示例</label>
<input
name="checkbox1"
class="checkbox1"
value="Item 3"
type="checkbox"
checked="checked"
/>
</div>
<button type="button" class="mui-btn" onclick="getVals()">获取值</button>
</div>
<script>
mui.init();
mui.plusReady(function () {});
function getVals() {
let res = getRadioRes("checkbox1");
if (res.length == 0) {
mui.toast("请选择");
return;
}
mui.toast(res, { duration: "long", type: "div" });
}
function getRadioRes(className) {
let rdsObj = document.getElementsByClassName(className);
let checkVal = [];
for (let i = 0; i < rdsObj.length; i++) {
if (rdsObj[i].checked) {
checkVal.push(rdsObj[i].value);
}
}
return checkVal;
}
</script>

代码效果

系统日期选择框

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
<button type="button" class="mui-btn" onclick="pickDate()">获取日期</button>
<script>
function pickDate() {
let dDate = new Date();
// 设置当前日期(不设置默认当前日期)
dDate.setFullYear(2016, 7, 16);

let minDate = new Date()
// 最小时间
minDate.setFullYear(2010, 1, 1);
let maxDate = new Date();
// 最大时间
maxDate.setFullYear(2020, 11, 31);
plus.nativeUI.pickDate(
function(e) {
let d = e.date;
mui.toast('您选择的日期是:' + d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate());
},
function(e) {
mui.toast('你没有选择日期');
}, {
title: '请选择日期',
date: dDate,
minDate: minDate,
maxDate: maxDate
})
}
</script>

代码效果

系统时间选择框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<button type="button" class="mui-btn" onclick="pickTime()">获取时间</button>
<script>
function pickTime(){
var myDate = new Date();
var h = myDate.getHours();
var min = myDate.getMinutes();
var dTime = new Date();
dTime.setHours(h,min);
plus.nativeUI.pickTime(function(e){
var d = e.date;
mui.toast('您选择的时间是' + d.getHours() + ":" + d.getMinutes());
},function(e){
mui.toast('您没有选择时间')
},{
title:'请选择时间',
is24Hour: true,
time:dTime
})
}
</script>

代码效果

对话框

警告框

1
2
3
4
5
6
7
8
<button type="button" class="mui-btn" onclick="alert()">警告框</button>
<script>
function alert() {
mui.alert('请不要随便点击', '警告消息', '确定', function() {
mui.toast('提示对话框上关闭后的回调函数');
})
}
</script>

确认框

1
2
3
4
5
6
7
8
9
10
11
12
<button type="button" class="mui-btn" onclick="confirm()">确认框</button>
<script>
function confirm() {
mui.confirm('MUI 是个好框架?', 'Hello MUI', new Array('否', '是'), function(e) {
if (e.index == 1) {
mui.toast('感谢您的支持')
} else {
mui.toast('MUI 没有得到你的认可,继续加油!');
}
})
}
</script>

输入对话框

1
2
3
4
5
6
7
8
9
10
11
12
<button type="button" class="mui-btn" onclick="prompt()">输入对话框</button>
<script>
function prompt() {
mui.prompt('请输入您对 MUI 的评语', '性能好', 'Hello MUI', new Array('取消', '确认'), function(e) {
if(e.index == 1){
mui.toast(e.value);
}else{
mui.toast('您取消了输入');
}
});
}
</script>

消息提示框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<button type="button" class="mui-btn" onclick="shorttoast()">短的消息提示框</button>
<button type="button" class="mui-btn" onclick="longtoast()">长的消息提示框</button>

<script>
function shorttoast(){
mui.toast('short 消息提示框',{
"duration" : "short"
})
}

function longtoast(){
mui.toast('long 消息提示框',{
"duration" : "long"
})
}
</script>

表单元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<form class="mui-input-group">
<div class="mui-input-row">
<label>用户名</label>
<input type="text" class="mui-input-clear" placeholder="请输入用户名">
</div>
<div class="mui-input-row">
<label>密码</label>
<input type="password" class="mui-input-password" placeholder="请输入密码">
</div>
<div class="mui-button-row">
<button type="button" class="mui-btn mui-btn-primary" >确认</button>
<button type="button" class="mui-btn mui-btn-danger" >取消</button>
</div>
</form>

表单

初始化

mui 在 mui.int() 中会自动初始化基本控件,但是动态添加的元素需要重新进行初始化

1
mui('.mui-input-row input').input();

轮播组件

连接模拟器

连接模拟器需要用到 ADB 。

1
2
3
4
5
// 连接模拟器端口
adb connect 127.0.0.1:62001

// 查看连接设备
adb devices

也可以通过 修改 HBuilder 运行配置 Android 模拟器端口 来实现连接。

注意:引入顺序。mui.min.js 需要在 demo渲染前 加载。如果在 demo 渲染后加载会莫名报错

1
2
3
4
5
6
7
8
9
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<meta name="HandheldFriendly" content="true"/>
<meta name="MobileOptimized" content="320"/>
<title></title>
<script src="js/mui.min.js"></script>
<link href="css/mui.min.css" rel="stylesheet" />
</head>

以上是我对下列视频及文章的归纳和总结。
MUI 视频教程


相关资料
HBuilder X 中使用模拟器进行 App 开发
ADB 常用命令及其用法大全
如何查看夜神模拟器的端口
DCloud appid 用途/作用/使用说明
Android 平台签名证书(.keystore)生成指南
Android 平台本地(离线)打包指南 - Android Studio
Android 平台云端打包证书使用说明
manifest.json 文档说明
MUI – plus初始化原理及plus is not defined,mui is not defined 错误汇总
Hbuilderx真机运行失败,失败原因:手机上没有信任本计算机的授权,请在手机上信任该

《Android App开发从入门到精通》

第1章 Android Studio的介绍以及使用

探索Android Studio

基于Gradle的灵活构建系统。• 快速且功能丰富的模拟器。• 可针对所有Android设备进行开发的统一环境。• Instant Run,可将变更推送到正在运行的应用,无须构建新的APK。• 帮助构建应用程序和导入示例代码以及GitHub集成。• 丰富的测试工具和框架。• 可捕捉性能、易用性、版本兼容性以及其他问题的Lint工具。• C++和NDK支持。• 内置对Google云端平台的支持,可轻松集成Google Cloud Messaging和App引擎。

书中案例源码下载
https://github.com/ansen666/book_source_code

phpstudy v8.0 的使用

大学时期,接触过 PHP,并且简单的使用过织梦,那时候用的工具是 XAMPP。PHP 忘得也差不多了,这次写 Ajax 例子的时候用到了 PHP,那就写一篇关于 phpstudy 常用操作吧。

WAMP、XAMPP、PHPstudy

WAMP 强烈推荐这款 :WAMP是基于Windows,Apache,MySQL和PHP的开放资源网络开发平台,Windows是微软的操作系统;Apache是最通用的网络服务器;mySQL是带有基于网络管理附加工具的关系数据库;
XAMPP:许多人通过他们自己的经验认识到安装 Apache 服务器是件不容易的事儿。如果您想添加 MySQL、PHP 和 Perl,那就更难了。XAMPP 是一个易于安装且包含 MySQL、PHP 和 Perl 的 Apache 发行版。XAMPP 的确非常容易安装和使用:只需下载,解压缩,启动即可。
PHPstudy:能够随意版本却换。


相关资料
phpstudy 官网
phpstudy v8.0 下载_安装步骤
phpstudy v8.0创建本地站点域名教程
phpstudy v8.0 网站创建与配置
phpstudy v8.0 php mysql多版本共存与切换教程
phpstudy v8.0 数据库管理工具phpadmin使用教程
phpstudy v8.0 FTP服务器设置
phpstudy v8.0 完全卸载图文教程

关于反编译

我最早听说过类似“反编译”的词汇是在汽车、飞机等领域,通过反编译来仿制汽车、飞机等,昨天面试听到面试公司技术骨干提到这个反编译。我觉得很神奇。索性出一篇文章来了解反编译。

反编译

反编译指通过他人软件进行“逆向分析、研究”工作,推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方式等设计要素,

某种特定情况下可能推导出源码。可以作为自己开发软件时的参考或直接用于自己的软件产品中。

这里我想到了,我在学校期间使用过一款扒网站的工具,跟反编译接近,工具是你输入一个网址然后通过这个网址来扒取网站的。我想大致原理是跟爬虫类似的,只不过爬虫抓取的是网站的数据,它抓取的是文件资源。

微信小程序反编译大致操作步骤,在模拟器中找到微信用微信打开某个小程序,小程序会被下载下来,然后找到下载下来的包,用ES 文件浏览器 把包复制到共享文件夹,然后通过 node 进行反编译,详细请看相关资料。


相关资料
科普:什么是编译与反编译
逆向反编译四大工具利器
知乎 反编译
反编译微信小程序
反编译获取任何微信小程序源码(完)
微信小程序反编译
如何将模拟器内的文件导出到电脑

Cordova 从入门到实践开发

简单认知 Cordova

什么是 Cordova

  • 一个移动应用开发框架
  • 本质是 HTML、CSS、JavaScript 外面包装个原生的壳
  • 出自于 Adobe 11 年收购的 PhoneGap,是驱动 PhoneGap 的核心引擎
  • 是 Apache 的顶级开源项目

Cordova 提供了一系列设备相关的 API,通过这组 API,移动应用能够以 JavaScript 访问原生的设备功能,如摄像头、麦克风。

阅读更多

Android 从入门到实践开发

P1 Android 开发概述和开发工具

Android 开发概述

Android 是一个开源的,基于Linux的移动设备操作系统,主要使用于移动设备,如智能手机和平板电脑。
Android 是由 Google 及其他公司带领的开放手机联盟开发的操作系统。

Android 编程是基于 Java 编程语言的,因此,如果你理解 Java 编程,这将利于你更好的学习 Android 应用程序开发。

Android官网
学习 Java

Android 开发是移动应用开发的表现形式之一。

Android、iOS、H5 App、Native + H5、RN、Ionic、MUI…

项目流程
完整项目最精简流程

Android 开发工具

  • 下载安装 Java JDK
  • 下载安装 Android Studio
阅读更多
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.