JavaScript 事件循环及异步原理(完全指北) - 梁音 - 博客园

mikel阅读(913)

来源: JavaScript 事件循环及异步原理(完全指北) – 梁音 – 博客园

引言

最近面试被问到,JS 既然是单线程的,为什么可以执行异步操作?
当时脑子蒙了,思维一直被困在 单线程 这个问题上,一直在思考单线程为什么可以额外运行任务,其实在我很早以前写的博客里面有写相关的内容,只不过时间太长给忘了,所以要经常温习啊:(浅谈 Generator 和 Promise 的原理及实现)

  1. JS 是单线程的,只有一个主线程
  2. 函数内的代码从上到下顺序执行,遇到被调用的函数先进入被调用函数执行,待完成后继续执行
  3. 遇到异步事件,浏览器另开一个线程,主线程继续执行,待结果返回后,执行回调函数

其实 JS 这个语言是运行在宿主环境中,比如 浏览器环境nodeJs环境

  • 在浏览器中,浏览器负责提供这个额外的线程
  • 在 Node 中,Node.js 借助 libuv 来作为抽象封装层, 从而屏蔽不同操作系统的差异,Node可以借助libuv来实现多线程。

而这个异步线程又分为 微任务 和 宏任务,本篇文章就来探究一下 JS 的异步原理以及其事件循环机制

为什么 JavaScript 是单线程的

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这样设计的方案主要源于其语言特性,因为 JavaScript 是浏览器脚本语言,它可以操纵 DOM ,可以渲染动画,可以与用户进行互动,如果是多线程的话,执行顺序无法预知,而且操作以哪个线程为准也是个难题。

所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

在 HTML5 时代,浏览器为了充分发挥 CPU 性能优势,允许 JavaScript 创建多个线程,但是即使能额外创建线程,这些子线程仍然是受到主线程控制,而且不得操作 DOM,类似于开辟一个线程来运算复杂性任务,运算好了通知主线程运算完毕,结果给你,这类似于异步的处理方式,所以本质上并没有改变 JavaScript 单线程的本质。

函数调用栈与任务队列

函数调用栈

JavaScript 只有一个主线程和一个调用栈(call stack),那什么是调用栈呢?

这类似于一个乒乓球桶,第一个放进去的乒乓球会最后一个拿出来。

举个栗子:

function a() {  
  console.log("I'm a!");
};

function b() {  
  a();
  console.log("I'm b!");
};

b();

执行过程如下所示:

  • 第一步,执行这个文件,此文件会被压入调用栈(例如此文件名为 main.js
    call stack
    main.js
  • 第二步,遇到 b() 语法,调用 b() 方法,此时调用栈会压入此方法进行调用:
    call stack
    b()
    main.js
  • 第三步:调用 b() 函数时,内部调用的 a() ,此时 a() 将压入调用栈:
    call stack
    a()
    b()
    main.js
  • 第四步:a() 调用完毕输出 I'm a!,调用栈将 a() 弹出,就变成如下:
    call stack
    b()
    main.js
  • 第五步:b()调用完毕输出I'm b!,调用栈将 b() 弹出,变成如下:
    call stack
    main.js
  • 第六步:main.js 这个文件执行完毕,调用栈将 b() 弹出,变成一个空栈,等待下一个任务执行:
    call stack

这就是一个简单的调用栈,在调用栈中,前一个函数在执行的时候,下面的函数全部需要等待前一个任务执行完毕,才能执行。

但是,有很多任务需要很长时间才能完成,如果一直都在等待的话,调用栈的效率极其低下,这时,JavaScript 语言设计者意识到,这些任务主线程根本不需要等待,只要将这些任务挂起,先运算后面的任务,等到执行完毕了,再回头将此任务进行下去,于是就有了 任务队列 的概念。

任务队列

所有任务可以分成两种,一种是 同步任务(synchronous),另一种是 异步任务(asynchronous) 。

同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有 "任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

所以,当在执行过程中遇到一些类似于 setTimeout 等异步操作的时候,会交给浏览器的其他模块进行处理,当到达 setTimeout 指定的延时执行的时间之后,回调函数会放入到任务队列之中。

当然,一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有任务执行完毕之后,接着去执行任务队列之中的回调函数。

用一张图来表示就是:

image-20181011232324823

上图中,调用栈先进行顺序调用,一旦发现异步操作的时候就会交给浏览器内核的其他模块进行处理,对于 Chrome浏览器来说,这个模块就是 webcore 模块,上面提到的异步API,webcore 分别提供了 DOM Binding 、 networktimer 模块进行处理。等到这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等栈中的任务执行完之后再去执行任务队列之中的回调函数。

我们先来看一个有意思的现象,我运行一段代码,大家觉得输出的顺序是什么:

  setTimeout(() => {
    console.log('setTimeout')
  }, 22)
  for (let i = 0; i++ < 2;) {
    i === 1 && console.log('1')
  }
  setTimeout(() => {
    console.log('set2')
  }, 20)
  for (let i = 0; i++ < 100000000;) {
    i === 99999999 && console.log('2')
  }

没错!结果很量子化:

QQ20181012-101019-HD

那么这实际上是一个什么过程呢?那我就拿上面的一个过程解析一下:

  • 首先,文件入栈

    image-20181012102607896

  • 开始执行文件,读取到第一行代码,当遇到 setTimeout 的时候,执行引擎将其添加到栈中。(由于字体太细我调粗了一点。。。)

    image-20181012103026018

  • 调用栈发现 setTimeout 是 Webapis中的 API,因此将其交给浏览器的 timer 模块进行处理,同时处理下一个任务。

image-20181012134531903

  • 第二个 setTimeout 入栈

    image-20181012134755318

  • 同上所示,异步请求被放入 异步API 进行处理,同时进行下一个入栈操作:

    image-20181012135048171

  • 在进行异步的同时,app.js 文件调用完毕,弹出调用栈,异步执行完毕后,会将回调函数放入任务队列:

    image-20181012140221038

  • 任务队列通知调用栈,我这边有任务还没有执行,调用栈则会执行任务队列里的任务:

    image-20181012140632679

    image-20181012140723756

上面的流程解释了浏览器遇到 setTimeout 之后究竟如何执行的,其实总结下来就是以下几点:

  1. 调用栈顺序调用任务
  2. 当调用栈发现异步任务时,将异步任务交给其他模块处理,自己继续进行下面的调用
  3. 异步执行完毕,异步模块将任务推入任务队列,并通知调用栈
  4. 调用栈在执行完当前任务后,将执行任务队列里的任务
  5. 调用栈执行完任务队列里的任务之后,继续执行其他任务

这一整个流程就叫做 事件循环(Event Loop)

那么,了解了这么多,小伙伴们能从事件循环上面来解析下面代码的输出吗?

  for (var i = 0; i < 10; i++) {
    setTimeout(() => {
      console.log(i)
    }, 1000)
  }
  console.log(i)

解析:

  • 首先由于 var 的变量提升,i 在全局作用域都有效
  • 再次,代码遇到 setTimeout 之后,将该函数交给其他模块处理,自己继续执行 console.log(i) ,由于变量提升,i 已经循环10次,此时 i 的值为 10 ,即,输出 10
  • 之后,异步模块处理好函数之后,将回调推入任务队列,并通知调用栈
  • 1秒之后,调用栈顺序执行回调函数,由于此时 i 已经变成 10 ,即输出10次 10

用下图示意:

image-20181012152514598

现在小伙伴们是否已经恍然大悟,从底层了解了为什么这个代码会输出这个内容吧:

image-20181012152640173

那么问题又来了,我们看下面的代码:

  setTimeout(() => {
    console.log(4)
  }, 0);
  new Promise((resolve) =>{
    console.log(1);
    for (var i = 0; i < 10000000; i++) {
      i === 9999999 && resolve();
    }
    console.log(2);
  }).then(() => {
    console.log(5);
  });
  console.log(3);

大家觉得这个输出是多少呢?

有小伙伴就开始分析了,promise 也是异步,先执行里面函数的内容,输出 1 和 2,然后执行下面的函数,输出 3 ,但 Promise 里面需要循环999万次,setTimeout 却是0毫秒执行,setTimeout 应该立即推入执行栈, Promise 后推入执行栈,结果应该是下图:

image-20181012161137385

实际上答案是 1,2,3,5,4 噢,这是为什么呢?这就涉及到任务队列的内部,宏任务和微任务。

宏任务和微任务

什么是宏任务和微任务

任务队列又分为 macro-task(宏任务) 与 micro-task(微任务) ,在最新标准中,它们被分别称为 task 与 jobs 。

  • macro-task(宏任务)大概包括:script(整体代码)setTimeoutsetIntervalsetImmediate(NodeJs)I/OUI rendering
  • micro-task(微任务)大概包括: process.nextTick(NodeJs)PromiseObject.observe(已废弃)MutationObserver(html5新特性)
  • 来自不同任务源的任务会进入到不同的任务队列。其中 setTimeout 与 setInterval 是同源的。

事实上,事件循环决定了代码的执行顺序,从全局上下文进入函数调用栈开始,直到调用栈清空,然后执行所有的micro-task(微任务),当所有的micro-task(微任务)执行完毕之后,再执行macro-task(宏任务),其中一个macro-task(宏任务)的任务队列执行完毕(例如setTimeout 队列),再次执行所有的micro-task(微任务),一直循环直至执行完毕。

解析

现在我就开始解析上面的代码。

  • 第一步,整体代码 script 入栈,并执行 setTimeout 后,执行 Promise

    image-20181013144141327

  • 第二步,执行时遇到 Promise 实例,Promise 构造函数中的第一个参数,是在new的时候执行,因此不会进入任何其他的队列,而是直接在当前任务直接执行了,而后续的.then则会被分发到micro-taskPromise队列中去。

    image-20181013144638756

    image-20181013144902587

  • 第三步,调用栈继续执行宏任务 app.js,输出3并弹出调用栈,app.js 执行完毕弹出调用栈:

    image-20181013145222565

    image-20181013145713234

  • 第四步,这时,macro-task(宏任务)中的 script 队列执行完毕,事件循环开始执行所有的 micro-task(微任务)

    image-20181013150040555

  • 第五步,调用栈发现所有的 micro-task(微任务) 都已经执行完毕,又跑去macro-task(宏任务)调用 setTimeout 队列:

    image-20181013150354612

  • 第六步,macro-task(宏任务) setTimeout 队列执行完毕,调用栈又跑去微任务进行查找是否有未执行的微任务,发现没有就跑去宏任务执行下一个队列,发现宏任务也没有队列执行,此次调用结束,输出内容1,2,3,5,4

那么上面这个例子的输出结果就显而易见。大家可以自行尝试体会。

总结

  1. 不同的任务会放进不同的任务队列之中。
  2. 先执行macro-task,等到函数调用栈清空之后再执行所有在队列之中的micro-task
  3. 等到所有micro-task执行完之后再从macro-task中的一个任务队列开始执行,就这样一直循环。
  4. 宏任务和微任务的队列执行顺序排列如下:
  5. macro-task(宏任务)script(整体代码)setTimeoutsetIntervalsetImmediate(NodeJs)I/OUI rendering
  6. micro-task(微任务)process.nextTick(NodeJs)PromiseObject.observe(已废弃)MutationObserver(html5新特性)

进阶举例

那么,我再来一些有意思一点的代码:

<script>
  setTimeout(() => {
    console.log(4)
  }, 0);
  new Promise((resolve) => {
    console.log(1);
    for (var i = 0; i < 10000000; i++) {
      i === 9999999 && resolve();
    }
    console.log(2);
  }).then(() => {
    console.log(5);
  });
  console.log(3);
</script>
<script>
  console.log(6)
  new Promise((resolve) => {
    resolve()
  }).then(() => {
    console.log(7);
  });
</script>

这一段代码输出的顺序是什么呢?

其实,看明白上面流程的同学应该知道整个流程,为了防止一些同学不明白,我再简单分析一下:

  • 首先,script1 进入任务队列(为了方便起见,我把两块script 命名为script1script2):

    image-20181013152218883

  • 第二步,script1 进行调用并弹出调用栈:

    image-20181013152506563

  • 第三步,script1执行完毕,调用栈清空后,直接调取所有微任务:

    image-20181013152844991

  • 第四步,所有微任务执行完毕之后,调用栈会继续调用宏任务队列:

    image-20181013153031374

  • 第五步,执行 script2,并弹出:

    image-20181013153426912

  • 第六步,调用栈开始执行微任务:

    image-20181013153503105

  • 第七步,调用栈调用完所有微任务,又跑去执行宏任务:

    image-20181013153654938

至此,所有任务执行完毕,输出 1,2,3,5,6,7,4

了解了上面的内容,我觉得再复杂一点异步调用关系你也能搞定:

setImmediate(() => {
    console.log(1);
},0);
setTimeout(() => {
    console.log(2);
},0);
new Promise((resolve) => {
    console.log(3);
    resolve();
    console.log(4);
}).then(() => {
    console.log(5);
});
console.log(6);
process.nextTick(()=> {
    console.log(7);
});
console.log(8);
//输出结果是3 4 6 8 7 5 2 1

image-20181013163225154

终极测试

setTimeout(() => {
    console.log('to1');
    process.nextTick(() => {
        console.log('to1_nT');
    })
    new Promise((resolve) => {
        console.log('to1_p');
        setTimeout(() => {
          console.log('to1_p_to')
        })
        resolve();
    }).then(() => {
        console.log('to1_then')
    })
})

setImmediate(() => {
    console.log('imm1');
    process.nextTick(() => {
        console.log('imm1_nT');
    })
    new Promise((resolve) => {
        console.log('imm1_p');
        resolve();
    }).then(() => {
        console.log('imm1_then')
    })
})

process.nextTick(() => {
    console.log('nT1');
})
new Promise((resolve) => {
    console.log('p1');
    resolve();
}).then(() => {
    console.log('then1')
})

setTimeout(() => {
    console.log('to2');
    process.nextTick(() => {
        console.log('to2_nT');
    })
    new Promise((resolve) => {
        console.log('to2_p');
        resolve();
    }).then(() => {
        console.log('to2_then')
    })
})

process.nextTick(() => {
    console.log('nT2');
})

new Promise((resolve) => {
    console.log('p2');
    resolve();
}).then(() => {
    console.log('then2')
})

setImmediate(() => {
    console.log('imm2');
    process.nextTick(() => {
        console.log('imm2_nT');
    })
    new Promise((resolve) => {
        console.log('imm2_p');
        resolve();
    }).then(() => {
        console.log('imm2_then')
    })
})
// 输出结果是:?

大家可以在评论里留言结果哟~

已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 'id',表 'ToolingD - 路漫漫 修远兮 - CSDN博客

mikel阅读(2349)

来源: 已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 ‘id’,表 ‘ToolingD – 路漫漫 修远兮 – CSDN博客

到excel中的数据导入SQL 时遇到的

报错的错是:

==============================

– 正在复制到 [ToolingDataSystem].[dbo].[Tool] (错误)
消息
信息 0x402090df: 数据流任务: 数据插入操作的最终提交已开始。
(SQL Server 导入和导出向导)

错误 0xc0202009: 数据流任务: 出现 OLE DB 错误。错误代码: 0x80004005。
已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“语句已终止。”。
已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 ‘id’,表 ‘ToolingDataSystem.dbo.Tool’;列不允许有空值。INSERT 失败。”。
(SQL Server 导入和导出向导)

信息 0x402090e0: 数据流任务: 数据插入操作的最终提交已结束。
(SQL Server 导入和导出向导)

错误 0xc0047022: 数据流任务: 组件“目标 – Tool”(251)的 ProcessInput 方法失败,错误代码为 0xC0202009。标识的这个组件从 ProcessInput 方法返回了一个错误。虽然该错误是此组件特有的,但却是致命的,将导致数据流任务停止运行。
(SQL Server 导入和导出向导)

错误 0xc0047021: 数据流任务: 线程“WorkThread0”已退出,错误代码为 0xC0202009。
(SQL Server 导入和导出向导)
消息
信息 0x402090df: 数据流任务: 数据插入操作的最终提交已开始。
(SQL Server 导入和导出向导)

错误 0xc0202009: 数据流任务: 出现 OLE DB 错误。错误代码: 0x80004005。
已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“语句已终止。”。
已获得 OLE DB 记录。源:“Microsoft SQL Native Client” Hresult: 0x80004005 说明:“不能将值 NULL 插入列 ‘id’,表 ‘ToolingDataSystem.dbo.Tool’;列不允许有空值。INSERT 失败。”。
(SQL Server 导入和导出向导)

信息 0x402090e0: 数据流任务: 数据插入操作的最终提交已结束。
(SQL Server 导入和导出向导)

错误 0xc0047022: 数据流任务: 组件“目标 – Tool”(251)的 ProcessInput 方法失败,错误代码为 0xC0202009。标识的这个组件从 ProcessInput 方法返回了一个错误。虽然该错误是此组件特有的,但却是致命的,将导致数据流任务停止运行。
(SQL Server 导入和导出向导)

错误 0xc0047021: 数据流任务: 线程“WorkThread0”已退出,错误代码为 0xC0202009。
(SQL Server 导入和导出向导)

==============================

不要勾选上面的”启用标识插入” ID 自增列 默认对应 忽略就好了
———————
作者:橙色阳光
来源:CSDN
原文:https://blog.csdn.net/sat472291519/article/details/19490673?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!

App被拒选择回复还是重新提审,如何选择最高效的应对方式? - 环信

mikel阅读(1246)

来源: App被拒选择回复还是重新提审,如何选择最高效的应对方式? – 环信

对于开发者来说,最揪心的不是 App 上传 App Store 审核被拒,而是被拒之后重新提审总要过好久才能得到新的反馈,而且结果依旧是……被拒……

今天,我们就请来了资深的过审达人「道长」来给大家分享——被拒之后怎样应对才是更高效的,直接回复还是重新提审?

一、两种被拒类型

通常来说,App 被拒不外乎两种情况:“元数据被拒”与“二进制被拒”。

那元数据和二进制分别指什么呢,刚接触 ASO 的新人可能不太了解,我们做一下简单的解释:

元数据,简单来说就是 iTC 后台(iutunesconnect.apple.com)上所有能编辑到的地方,我们都可以称之为“元数据”,比如标题、副标题、关键字域、截图等。

二进制全称是“二进制文件”,也可以说就是苹果 iOS 应用的 ipa 包(iOS 的安装包后缀为 ipa,安卓安装包后缀为apk)。一般二进制文件是由团队中的技术人员打包后上传至 iTC,如果 App 被拒,有一些情况是需要重新提交二进制文件的(如下图)。

001.jpg

002.jpg

  而且苹果在两种拒绝类型中,给开发者回复的拒绝消息的内容也有所不同。

元数据被拒,苹果回复的消息会包含以下内容:

“由于您的元数据被拒绝,您可修改元数据内容后在此处回复我,收到您的回复后,我们会继续审核您的应用。”

二进制被拒,苹果回复的消息会包含以下内容:

“由于您的二进制文件被拒绝,您需要重新修改二进制文件后上传到 iTC 后台,我方收到您的重新提交后,会继续审核您的应用。”

整体回复内容大致一样。区别在于,① 苹果拒绝了你的元数据,你可以修改元数据后直接回复,苹果收到后会继续审核;② 但是拒绝二进制的大多数情况下为 ipa 包本身的问题,例如 ipv6,bug 等等,所以苹果会要求开发者更改应用后重新打包,然后重新提审至 iTC 后台。

二、不同应对方式

被拒之后,开发者面临这两种不同情况,处理方式也有所不同——选择「回复苹果」还是「重新提交」, 才能在最有效的时间让苹果重新审核我们提交的 ipa 包。

元数据被拒:

修改原数据即可,比如标题问题(违反条款 2.3.7),直接修改标题后保存,然后对被拒消息进行回复。这里有回复的模板,大家可借鉴:

尊敬的苹果开发者审核您好;

我方已修正贵方所提出问题,

请重新审核;

诚挚的问候!

修改完元数据之后点击这里回复

003.jpg

  二进制被拒:

二进制被拒也要看被拒原因选择是否重新打包。

比如因为 PLA 1.2,这种情况,有的可以直接在后台回复相关内容即可(比如相关的资质附件);

如果因为包本身的问题,也要看 ipa 包有没有被“写死”。

如果被写死,违规的内容如果不修改 ipa 包就无法更改的话,那就重新打包提审 ;

如果没有写死,或者 IPv6 的问题,我们可以直接在服务端修改,不必修改 ipa 包也不用重新提交,改完服务端的部分就可以直接回复苹果了。

PS:建议大家在产品上线前和技术进行沟通,关于部分内容、特定页面(主要是一些被审查的敏感位置)都可以留有灵活控制的空间,实现不重新打包就可以重新提审,减少“写死”的情况出现。

另外,有些二进制被拒的情况,是否要修改 ipa 包还是要更灵活判断的。比如产品因为 4.3 被拒,只回复苹果、只修改服务端内容往往还是会被拒,最终还要视情况修改 App 本身的视觉和代码等部分。

这里是与苹果沟通时的回复模板,大家可以借鉴

IPv6 等内容的回复:

尊敬的苹果开发者审核;

您好,我方已在服务端修复此问题,

请重新审核;

诚挚的问候

Bug 等内容的回复:

尊敬的苹果开发者审核您好;

我方已修正贵方所提出问题,

请重新审核;

诚挚的问候!

为什么我们要直接回复???

正常情况下,重新提交 ipa 审核会再次进入审核队列,需要排队,因为苹果需要重新安装或者说重走审核流程;而直接回复,苹果会在目前有的问题上,检查问题,然后继续审核。并且回复的内容,会被苹果在 30 分钟至最多 1 天内回复处理;重新提审则需要 1-2 天才会进入审核。

(PS:现苹果审核比较快速,正常情况下1天即会审核,特殊情况除外。但避免不必要麻烦,建议大家后台回复会比较便利。)

苹果近段时间审核基本上是我们当天晚上提交,凌晨都会审核;但最近审核都普遍比较慢,在 2 天到 4 天以至于更多,所以在被拒之后,如果能选择比较便捷的方式直接回复的话,在时间上都是比较节约的。

最后关于一些常见问题:

①二进制被拒情况下,如果不重新提审,而是直接选择构建新版本,是否苹果收到回复后会审核到新的版本?

经测试,回复内容后,苹果会继续审核,但审核人员安装的用来审核的版本,还是旧的版本,并非“构建版本”更换后的“新版本”,且继续进行常规审核流程流程。不出意外的话,还是会回复上一版出现的问题。

 

004.jpg
005.jpg

②二进制or元数据被拒情况下,如果回复了内容,然后又重新提审了,苹果会如何处理?审核时间呢?(这里同样适用于申诉后重新提审的情况)

经测试,回复并重新提审后,会出现 2 个情况:

第一种情况是苹果审核会回复一版内容,大致为“我已收到您提交的审核请求,会继续您提交的新版应用”并且提审的会重新排队,我们需要等待审核;

第二种情况是苹果审核未回复,在最短的时间内重新审核了,结果被拒或者过审(也有回复第一条的内容后,最短的时间内审核的,时间会在 1 天内,或者几小时、几分钟内。)。

最新VIP视频解析网站搭建教程(附源码) - kaixin09o的博客 - CSDN博客

mikel阅读(1593)

ofzb

来源: 最新VIP视频解析网站搭建教程(附源码) – kaixin09o的博客 – CSDN博客

今天交大家搭建一个VIP视频解析网站,另赠送源码,毫无保留的奉献给大家。

这是演示站:www.wuyyb.com

这个VIP视频解析网站可以解析优酷、爱奇艺、腾讯视频、乐视视频等等,要VIP会员才可以看的电视剧、电影。

用此神器看VIP视频不需要花钱,也不需要注册,非常简单好用。

下面我就交大家怎么搭建。

1.首先需要注册域名(随便你在那个域名注册商那里注册都可以,价钱都差不多,我这里就不打广告了。)

2.注册一个虚拟主机,一般100M的主机空间就可以。可以用香港的主机或者国外的主机,因为这些主机是不需要备案的。如果你想备案也可以用国内主机。当然也可以用云主机或者vps服务器(土豪请随意)。

3.下载源码。

源码下载链接:https://pan.baidu.com/s/14BRBEQ6p4jVuw0FAAsplpg  密码:ofzb

4.源码下载好后解压缩,然后用FTP上传到服务器根目录下。(如图)

到这里就差不多了,上传完后浏览自己的域名就可以看到网站了.

附:下图中的两条网址是打开网页自动播放的视频网址,可以根据自己的喜好更换,在index.html更换。
——————— 本文来自 kaixin09o 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/kaixin09o/article/details/79797519?utm_source=copy

iOS应用审核的时隐私政策模板 - xiaobo0134的专栏 - CSDN博客

mikel阅读(1213)

来源: iOS应用审核的时隐私政策模板 – xiaobo0134的专栏 – CSDN博客

隐私政策 

AAA尊重并保护所有使用服务用户的个人隐私权。为了给您提供更准确、更有个性化的服务,AAA会按照本隐私权政策的规定使用和披露您的个人信息。但AAA将以高度的勤勉、审慎义务对待这些信息。除本隐私权政策另有规定外,在未征得您事先许可的情况下,AAA不会将这些信息对外披露或向第三方提供。AAA会不时更新本隐私权政策。 您在同意AAA服务使用协议之时,即视为您已经同意本隐私权政策全部内容。本隐私权政策属于AAA服务使用协议不可分割的一部分。

1. 适用范围

a) 在您注册AAA帐号时,您根据AAA要求提供的个人注册信息;

b) 在您使用AAA网络服务,或访问AAA平台网页时,AAA自动接收并记录的您的浏览器和计算机上的信息,包括但不限于您的IP地址、浏览器的类型、使用的语言、访问日期和时间、软硬件特征信息及您需求的网页记录等数据;

c) AAA通过合法途径从商业伙伴处取得的用户个人数据。

您了解并同意,以下信息不适用本隐私权政策:

a) 您在使用AAA平台提供的搜索服务时输入的关键字信息;

b) AAA收集到的您在AAA发布的有关信息数据,包括但不限于参与活动、成交信息及评价详情;

c) 违反法律规定或违反AAA规则行为及AAA已对您采取的措施。

2. 信息使用

a) AAA不会向任何无关第三方提供、出售、出租、分享或交易您的个人信息,除非事先得到您的许可,或该第三方和AAA(含AAA关联公司)单独或共同为您提供服务,且在该服务结束后,其将被禁止访问包括其以前能够访问的所有这些资料。

b) AAA亦不允许任何第三方以任何手段收集、编辑、出售或者无偿传播您的个人信息。任何AAA平台用户如从事上述活动,一经发现,AAA有权立即终止与该用户的服务协议。

c) 为服务用户的目的,AAA可能通过使用您的个人信息,向您提供您感兴趣的信息,包括但不限于向您发出产品和服务信息,或者与AAA合作伙伴共享信息以便他们向您发送有关其产品和服务的信息(后者需要您的事先同意)。

3. 信息披露

在如下情况下,AAA将依据您的个人意愿或法律的规定全部或部分的披露您的个人信息:

a) 经您事先同意,向第三方披露;

b) 为提供您所要求的产品和服务,而必须和第三方分享您的个人信息;

c) 根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;

d) 如您出现违反中国有关法律、法规或者AAA服务协议或相关规则的情况,需要向第三方披露;

e) 如您是适格的知识产权投诉人并已提起投诉,应被投诉人要求,向被投诉人披露,以便双方处理可能的权利纠纷;

f) 在AAA平台上创建的某一交易中,如交易任何一方履行或部分履行了交易义务并提出信息披露请求的,AAA有权决定向该用户提供其交易对方的联络方式等必要信息,以促成交易的完成或纠纷的解决。

g) 其它AAA根据法律、法规或者网站政策认为合适的披露。

4. 信息存储和交换

AAA收集的有关您的信息和资料将保存在AAA及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或AAA收集信息和资料所在地的境外并在境外被访问、存储和展示。

5. Cookie的使用

a) 在您未拒绝接受cookies的情况下,AAA会在您的计算机上设定或取用cookies

,以便您能登录或使用依赖于cookies的AAA平台服务或功能。AAA使用cookies可为您提供更加周到的个性化服务,包括推广服务。  b) 您有权选择接受或拒绝接受cookies。您可以通过修改浏览器设置的方式拒绝接受cookies。但如果您选择拒绝接受cookies,则您可能无法登录或使用依赖于cookies的AAA网络服务或功能。

c) 通过AAA所设cookies所取得的有关信息,将适用本政策。

6. 信息安全

a) AAA帐号均有安全保护功能,请妥善保管您的用户名及密码信息。AAA将通过对用户密码进行加密等安全措施确保您的信息不丢失,不被滥用和变造。尽管有前述安全措施,但同时也请您注意在信息网络上不存在“完善的安全措施”。

b) 在使用AAA网络服务进行网上交易时,您不可避免的要向交易对方或潜在的交易对方披露自己的个人信息,如联络方式或者邮政地址。请您妥善保护自己的个人信息,仅在必要的情形下向他人提供。如您发现自己的个人信息泄密,尤其是AAA用户名及密码发生泄露,请您立即联络AAA客服,以便AAA采取相应措施。

——————— 本文来自 博BOBO 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/xiaobo0134/article/details/82385149?utm_source=copy

Asp.Net SignalR - 准备工作 - 一路NET - 博客园

mikel阅读(1563)

来源: Asp.Net SignalR – 准备工作 – 一路NET – 博客园

原文:Asp.Net SignalR – 准备工作

实时通讯

在做Web的时候经常会有客户端和服务端实时通讯的需求,比如即时聊天等。目前实时通讯有很多种规范和实现方式,但是每一个对浏览器的兼容性或者性能都不是很完美。

目前主流的解决方案有

WebSocket   使用ws协议基于tcp

SSE  订阅服务器事件方式

长连接

轮询

而sigualR对这些都进行了封装,使得我们不用去关心到底是什么实现,是websocket还是sse,它会根据当前浏览器的兼容性来做做好的选择。

 

Simple Demo

创建一个owin的启动类,然后添加一个signalR的永久链接类 起名为 MyConnetion1

复制代码
public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888
            app.MapSignalR<MyConnection1>("/myconnection");
        }
    }
复制代码

在html中代码也是非常简单的

复制代码
<script type="text/javascript">
        let conn = $.connection("/myconnection");
        conn.logging = true;
        conn.start().done(function (data) {
        });
    </script>
复制代码

先不用管是做什么,把连接的loggin设置为true,运行项目就可以看到如下的log信息,可以明确的看到这里使用的是WebSocket

image

 

不得不说的Owin

可以看到之前的simple demo中创建了一个owin的起始类,这是owin是个什么东西呢

owin是一个规范,下面引用的是博客员dudu的图与一些解释

owin是针对 .net平台的开放web接口,owin就是.net web应用程序与web服务器之间的接口。owin使得应用程序不依赖于web服务器,它们通过接口来进行工作,而更换其它的web服务器只要符合 owin 接口的规范都是没有问题的。

owin是典型的适配器模式

image

owin把Web应用的解决方案进行了分层

host  宿主

service  服务器  服务器可以是iis也以是其它的

Middleware  中间件,  中间件用来丰富service达到功能添加

application  应用

 

katana

katana就是owin规范的实现,而.net的Mvc项目也已经实现了owin规范,所以那些喷.net不能跨平台的,都是to young。姿势水平还需要提升啊。

Startup类是我们的owin入口点,我们可以在里写入代码把中间件打入owin管道

 

下面再进行盗图(来自 http://www.cnblogs.com/GuZhenYin/p/5201388.html) 第一张图是一个规范而第二张图是真正已经实现的,也就是我们的SignalR

imageimage

解决为'*********' 的游标已存在问题 - 挑战 - 博客园

mikel阅读(1142)

来源: 解决为’*********’ 的游标已存在问题 – 挑战 – 博客园

出现名为’MM_CURSOR’ 的游标已存在。 
一般出现这样的问题是: 
1:游标没有    –关闭 释放 
如: 

SQL代码 
  1. –关闭游标
  2.       CLOSE MM_CURSOR
  3.         –释放游标
  4.      DEALLOCATE MM_CURSOR

2:游标已存在同名情况,此时就需要在定义游标时申明一个局部的游标 
如: 

SQL代码 
  1. /*检索已经配置好的新村镇的所有乡级部门*/
  2.    —申明游标
  3. DECLARE deptCursor CURSOR
  4.    local FOR
  5.    SELECT  deptname, deptsimplename,distid, deptuncode,deptqueryno,ifreport,deptsort,enable,deptfloor,deptcharacter,caseSMSFlag,deptType
  6.    FROM t_department
  7.    where  PARENTID=250 and deptType=‘2’

其实我的情况都不是这样,只是在使用嵌套多层循环操作时把两个游标全部放在存储过程末后 

Sql代码 
  1. –关闭游标
  2.       CLOSE MM_CURSOR
  3.         –释放游标
  4.      DEALLOCATE MM_CURSOR
  5.    –关闭游标–释放游标
  6. CLOSE deptCursor
  7.    –释放游标
  8. DEALLOCATE deptCursor

没有及时关闭导致问题出现! 
正确代码如下 

Sql代码 
  1. set ANSI_NULLS ON
  2. set QUOTED_IDENTIFIER ON
  3. go
  4. —drop  PROCEDURE copyDept
  5. ALTER PROCEDURE [dbo].[copyDept]
  6.     as
  7.     declare @deptCode varchar(20)
  8.     declare @deptname varchar(10)
  9.     declare @deptsimplename varchar(100)
  10.     declare @distid bigint
  11.     declare @deptuncode varchar(100)
  12.     declare @deptqueryno varchar(100)
  13.     declare @ifreport varchar(4)
  14.     declare @deptsort int
  15.     declare @enable varchar(6)
  16.     declare @deptfloor smallint
  17.     declare @deptcharacter varchar(50)
  18.     declare @caseSMSFlag varchar(4)
  19.     declare @deptType varchar(1)
  20.     declare @DeNo bigint
  21.     set nocount on
  22.     begin
  23.     set  @deptcode = ‘2000000’
  24.     /*检索已经配置好的新村镇的所有乡级部门*/
  25.     —申明游标
  26.     DECLARE deptCursor CURSOR
  27.     local FOR
  28.     SELECT  deptname, deptsimplename,distid, deptuncode,deptqueryno,ifreport,deptsort,enable,deptfloor,deptcharacter,caseSMSFlag,deptType
  29.     FROM t_department
  30.     where  PARENTID=250 and deptType=‘2’
  31.     —打开游标
  32.     OPEN deptCursor
  33.     –循环取出游标    
  34.     FETCH NEXT FROM deptCursor INTO @deptname,@deptsimplename,@distid,@deptuncode,@deptqueryno,@ifreport,@deptsort,@enable,@deptfloor,@deptcharacter,@caseSMSFlag,@deptType
  35.     while (@@FETCH_STATUS = 0)
  36.         begin
  37.             /*检索乡镇行政部门:如赵集镇,龙王乡…*/
  38.             —申明游标
  39.             Declare MM_CURSOR CURSOR
  40.             local  FOR
  41.             Select DEPTID from t_department where  ENABLE= ‘启用’ and DISTID = 1 and deptType=0 and deptid !=250—demo,except 250 — and PARENTID =288–and deptid not in (243,244)–and is_convenience=@tjType jc_dreaming
  42.             Order by DEPTCODE /**ONLY VALID DEPARTMENT */
  43.             — 打开游标
  44.             open MM_CURSOR
  45.             –循环取出游标    
  46.             FETCH NEXT FROM MM_CURSOR  INTO @DeNo
  47.             while (@@FETCH_STATUS = 0)
  48.                 BEGIN
  49.                  set @deptcode = convert(varchar(20),cast(@deptcode as int)+1)
  50.                 print(@deptcode)
  51.                  INSERT INTO T_DEPARTMENT (deptcode, deptname, deptsimplename,distid, deptuncode,deptqueryno,ifreport,deptsort,enable,deptfloor,PARENTID,deptcharacter,caseSMSFlag,deptType)
  52.                                     VALUES (@deptcode,@deptname,@deptsimplename,@distid,@deptuncode,@deptqueryno,@ifreport,@deptsort,@enable,@deptfloor,@DeNo,@deptcharacter,@caseSMSFlag,@deptType)
  53.                 FETCH NEXT FROM MM_CURSOR INTO @DeNo
  54.                 END
  55.             –关闭游标
  56.           CLOSE MM_CURSOR
  57.          –释放游标
  58.          DEALLOCATE MM_CURSOR
  59.         FETCH NEXT FROM deptCursor INTO @deptname,@deptsimplename,@distid,@deptuncode,@deptqueryno,@ifreport,@deptsort,@enable,@deptfloor,@deptcharacter,@caseSMSFlag,@deptType
  60.                                         –@deptname,@deptsimplename,@distid,@deptuncode,@deptqueryno,@ifreport,@deptsort,@enable,@deptfloor,@deptcharacter,@caseSMSFlag,@deptType
  61.         end
  62.     end
  63.     –关闭游标
  64.     CLOSE deptCursor
  65.     –释放游标
  66.     DEALLOCATE deptCursor

此外,在刚开始调用存储过程还遇到一个问题:程序处于正在查询状态,近一个小时,我想,数据还没那么复杂,可能出现死循环或某个游标没有移动… 
可是看了代码,没有出现这样的情况, 
经同事指点: 

Sql代码 
  1. —申明游标
  2.             Declare MM_CURSOR CURSOR
  3.             local  FOR
  4.             Select DEPTID from t_department where  ENABLE= ‘启用’ and DISTID = 1 and deptType=0 and deptid !=250—demo,except 250 — and PARENTID =288–and deptid not in (243,244)–and is_convenience=@tjType jc_dreaming
  5.             Order by DEPTCODE /**ONLY VALID DEPARTMENT */
  6.             — 打开游标
  7.             open MM_CURSOR
  8.             –循环取出游标    
  9.             FETCH NEXT FROM MM_CURSOR  INTO @DeNo
  10.             while (@@FETCH_STATUS = 0)
  11.                 set @deptcode = convert(varchar(20),cast(@deptcode as int)+1)   //把此行代码移至begin代码内即可
  12.                 BEGIN
  13.                 print(@deptcode)
  14.                  INSERT INTO T_DEPARTMENT (deptcode, deptname, deptsimplename,distid, deptuncode,deptqueryno,ifreport,deptsort,enable,deptfloor,PARENTID,deptcharacter,caseSMSFlag,deptType)
  15.                                     VALUES (@deptcode,@deptname,@deptsimplename,@distid,@deptuncode,@deptqueryno,@ifreport,@deptsort,@enable,@deptfloor,@DeNo,@deptcharacter,@caseSMSFlag,@deptType)
  16.                 FETCH NEXT FROM MM_CURSOR INTO @DeNo
  17.                 END
  18.             –关闭游标
  19.           CLOSE MM_CURSOR
  20.          –释放游标
  21.          DEALLOCATE MM_CURSOR

Decimal 保留2为小数。 - calm_水手 - 博客园

mikel阅读(911)

来源: Decimal 保留2为小数。 – calm_水手 – 博客园

保留小数位的方法,有下面这几种:Math.Round();Decimal.Round();.ToString(“#0.00”);.ToString(“N2”)

其中.Round()方法是四舍六入(五五才进一);.ToString(“#0.00”)和.ToString(“N2”)为四舍五入。

如Decimal  num=1.2355m;

.Rountd(num,2)=1.24; .ToString(“#0.00”)和.ToString(“N2”)= 1.24;

Decimal num=1.2354m;

.Rountd(num,2)=1.23; .ToString(“#0.00”)和.ToString(“N2”)= 1.24;

取整

.ToString(“#0”);

Docker系列之入门篇 - 潇十一郎 - 博客园

mikel阅读(1289)

来源: Docker系列之入门篇 – 潇十一郎 – 博客园

Docker 是世界领先的软件容器平台。开发人员利用 Docker 可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用 Docker 可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用 Docker 可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为 Linux 和 Windows Server 应用发布新功能。

0|1认识容器

有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效、轻量、自给自足,还能保证部署在任何环境中的软件都能始终如一地运行。

以上是官方的解释。嗯,看完是不是仍然一脸懵逼,不要紧,我们先看来一张图:

 

推荐 先记忆 后理解,记忆什么?Docker组成的三个基本概念:

镜像 (Image):Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

容器(Container):容器镜像是轻量的、可执行的独立软件包,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。

仓库(Repository):如果你使用过git和github就很容易理解Docker的仓库概念。Docker 仓库的概念跟Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。

Docker 仓库是用来包含镜像的位置,Docker提供一个注册服务器(Register)来保存多个仓库,每个仓库又可以包含多个具备不同tag的镜像。Docker运行中使用的默认仓库是 Docker Hub 公共仓库。

仓库支持的操作类似git,当用户创建了自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以了

我们来看一下Docker的生命周期:

如上图所示,容器是由镜像实例化而来的,这和我们学习的面向对象的概念十分相似,我们可以把镜像想象成,把容器想象成类经过实例化后的对象,先有了”镜像类”,然后可以实例化多个不同的容器1,容器2、容器3……,这样就非常好理解镜像和容器的关系了。

2|0安装Docker-CE

2|1 CentOS安装

⑴卸载旧版本(较旧版本的Docker被称为dockerdocker-engine。如果已安装这些,请卸载它们以及相关的依赖项。)

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine

已安装docker,会进行删除操作:

⑵安装docker -ce(社区免费版)

安装方式,使用存储库安装,在新主机上首次安装Docker CE之前,需要设置Docker存储库。之后,可以从存储库安装和更新Docker。

安装所需的包。yum-utils提供了yum-config-manager 效用,并device-mapper-persistent-datalvm2由需要 devicemapper存储驱动程序。

$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

 

⑶使用以下命令设置稳定存储库。即使还想从边缘或测试存储库安装构建,始终需要稳定的存储 库

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

⑷可选:启用边缘和测试存储库。这些存储库包含在docker.repo上面的文件中,但默认情况下处于禁用状态。可以将它们与稳定存储库一起启用。

$ sudo yum-config-manager --enable docker-ce-edge

$ sudo yum-config-manager --enable docker-ce-test

//您可以通过运行带有标志的命令来禁用边缘或测试存储库 。要重新启用它,请使用该标志。以下命令禁用边缘存储库。yum-config-manager--disable--enable

$ sudo yum-config-manager --disable docker-ce-edge

//注意:从Docker 17.06开始,稳定版本也会被推送到边缘并测试存储库。

⑸安装最新版本的Docker CE

$ sudo yum install docker-ce

 

⑹启动Docker

$ sudo systemctl start docker

docker通过运行hello-world 映像验证是否已正确安装

$ sudo docker run hello-world

至此,docker在CentOS上的安装已经全部完成,我们可以查看下当前安装的docker版本信息

2|2Windows安装

https://docs.docker.com/docker-for-windows/install/

3|0Docker常用命令汇总

 

未完待续。。。docker全套VIP课程资源可以加入.NET全栈开发群 免费获取,群号:523490820