前言

本文主要参考了JavaScript Promise迷你书,链接在文末与其他参考一起列出。

promise基础

Promise是异步编程的一种解决方案。ES6 Promise的规范来源于Promises/A+社区,它有很多版本的实现。

Promise比传统的解决方案(回调函数和事件)更合理和更强大,可以避免回调地狱。使用Promise来统一处理异步操作,更具语义化、易于理解、有利维护。

Promise接口的基本思想是让异步操作返回一个Promise对象,我们可以对这个对象进行一些操作。

三种状态和两种变化途径

Promise对象只有三种状态。

  • 异步操作“未完成”,promise对象刚被创建后的初始化状态(unresolved,Promises/A+中称pending)
  • 异步操作“已完成”(resolved,Promises/A+中称fulfilled)
  • 异步操作“失败”(rejected)

这三种的状态的变化途径只有两种。

  • 异步操作从“未完成”到“已完成”
  • 异步操作从“未完成”到“失败”。

这种变化只能发生一次,一旦当前状态变为“已完成”或“失败”,就意味着不会再有新的状态变化了。因此,Promise对象的最终结果只有两种。

异步操作成功,Promise对象传回一个值,状态变为resolved。

异步操作失败,Promise对象抛出一个错误,状态变为rejected。

api简介

目前主要有三种类型

1) 构造函数(Constructor)

创建一个promise实例:

var promise = new Promise(function (resolve, reject) {// 异步处理// 处理结束后、调用resolve 或 reject
})

2) 实例方法(Instance Method)

promise.then(onFulfilled, onRejected)promise.catch(onRejected)

3) 静态方法(Static Method)

Promise.all()、 Promise.race()、Promise.resolve()、Promise.reject()

创建promise对象

给Promise构造函数传递一个函数fn作为参数实例化即可。这个函数fn有两个参数(resolve和reject),在fn中指定异步等处理:

  • 处理结果正常的话,调用resolve(处理结果值)
  • 处理结果错误的话,调用reject(Error对象)。
    // 创建promise对象基本形式var promise = new Promise(function (resolve, reject) {// ... some codeif (/* 异步操作成功 */) {resolve(value)} else {reject(error)}})// 将图片加载转为promise形式var preloadImage = function (path) {return new Promise(function (resolve, reject) {var image = new Image()image.onload  = resolveimage.onerror = rejectimage.src = path})}// 创建XHR的promise对象function getURL (URL) {return new Promise(function (resolve, reject) {var req = new XMLHttpRequest()req.open('GET', URL, true)req.onload = function () {if (req.status === 200) {resolve(req.responseText)} else {reject(new Error(req.statusText))}}req.onerror = function () {reject(new Error(req.statusText))}req.send()})}// 运行示例var URL = 'http://httpbin.org/get'getURL(URL).then(function onFulfilled (value){console.log(value)}).catch(function onRejected (error){console.error(error)})

getURL只有在通过XHR取得结果状态为200时才会调用resolve。也就是只有数据取得成功时,而其他情况(取得失败)时则会调用reject方法。

resolve(req.responseText)在response的内容中加入了参数。resolve方法的参数并没有特别的规则,基本上把要传给回调函数参数放进去就可以了。(then方法可以接收到这个参数值)

为promise对象添加处理方法

为promise对象添加处理方法主要有以下两种:

  • promise对象被resolve时的处理(onResolved)
  • promise对象被reject时的处理(onRejected)

被resolve后的处理,可以在.then方法中传入想要调用的函数:

var URL = 'http://httpbin.org/get'
getURL(URL).then(function onResolved(value){ console.log(value)
})

被reject后的处理,可以在.then的第二个参数或者是在.catch方法中设置想要调用的函数。

var URL = 'http://httpbin.org/status/500'
getURL(URL).then(function onResolved(value){console.log(value)}).catch(function onRejected(error){ console.error(error)})

.catch只是promise.then(undefined, onRejected)的别名而已,如下代码也可以完成同样的功能。

getURL(URL).then(onResolved, onRejected)

Promise.resolve

1)new Promise的快捷方式

静态方法Promise.resolve(value)可以认为是new Promise()方法的快捷方式。Promise.resolve(value)返回一个状态由给定value决定的Promise对象。如果该值是一个Promise对象,则直接返回该对象;如果该值是thenable对象(见下面部分2),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为resolved,并且将该value传递给对应的then方法。

所以和new Promise()方法并不完全一致。Promise.resolve接收一个promise对象会直接返回这个对象。而new Promise()总是新生成一个promise对象。

var p1 = Promise.resolve(1)var p2 = Promise.resolve(p1)var p3 = new Promise(function (resolve, reject) {resolve(p1)
})console.log(p1 === p2) // true
console.log(p1 === p3) // false

常用Promise.resolve()快速初始化一个promise对象。

Promise.resolve(42).then(function (value) {console.log(value)
})

2)Promise.resolve方法另一个作用就是将thenable对象转换为promise对象。

什么是thenable对象?Thenable对象可以认为是类Promise对象,拥有名为.then方法的对象。和类数组的概念相似。

有哪些thenable对象?主要是ES6之前有许多库实现了Promise,其中有很多与ES6 Promise规范并不一致,我们称这些与ES6中的promise对象类似而又有差异的对象为thenable对象。如jQuery中的ajax()方法返回的对象。

// 将thenable对象转换promise对象
var promise = Promise.resolve($.ajax('/json/comment.json')) // => promise对象
promise.then(function (value) {console.log(value)
})

Promise.reject()

Promise.reject(error)是和Promise.resolve(value)类似的静态方法,是new Promise()方法的快捷方式。

比如Promise.reject(new Error('出错了'))就是下面代码的语法糖形式:

new Promise(function (resolve, reject) {reject(new Error('出错了'))
})

Promise.all

Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。

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

上面代码中,Promise.all方法接受一个数组作为参数,p1、p2、p3都是Promise实例,如果不是,就会先调用Promise.resolve方法,将参数转为Promise实例,再进一步处理。

p的状态由p1、p2、p3决定,分成两种情况。

(1)只有p1、p2、p3的状态都变成resolved,p的状态才会变成resolved,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

传递给Promise.all的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。

// `delay`毫秒后执行resolve
function timerPromisefy (delay) {return new Promise(function (resolve) {setTimeout(function () {resolve(delay)}, delay)})
}var startDate = Date.now()// 所有promise变为resolve后程序退出
Promise.all([timerPromisefy(1),timerPromisefy(32),timerPromisefy(64),timerPromisefy(128)
]).then(function (values) {console.log(Date.now() - startDate + 'ms')// 约128msconsole.log(values)   // [1, 32, 64, 128]
})

从上述结果可以看出,传递给Promise.all的promise并不是一个个的顺序执行的,而是同时开始、并行执行的。

如果这些promise全部串行处理的话,那么需要等待1ms → 等待32ms → 等待64ms → 等待128ms ,全部执行完毕需要约225ms的时间。

Promise.race

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

与Promise.all类似,但是只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的回调函数。

// `delay`毫秒后执行resolve
function timerPromisefy(delay) {return new Promise(function (resolve) {setTimeout(function () {resolve(delay)}, delay)})
}// 任何一个promise变为resolve或reject的话程序就停止运行
Promise.race([timerPromisefy(1),timerPromisefy(32),timerPromisefy(64),timerPromisefy(128)
]).then(function (value) {console.log(value) // => 1
})

下面我们再来看看在第一个promise对象变为确定(resolved)状态后,它之后的promise对象是否还在继续运行:

var winnerPromise = new Promise(function (resolve) {setTimeout(function () {console.log('this is winner')resolve('this is winner')}, 4)
})var loserPromise = new Promise(function (resolve) {setTimeout(function () {console.log('this is loser')resolve('this is loser')}, 1000)
})// 第一个promise变为resolve后程序停止
Promise.race([winnerPromise, loserPromise]).then(function (value) {console.log(value) // => 'this is winner'
})

执行上面代码的话,我们会看到winnter和loser promise对象的setTimeout方法都会执行完毕,console.log也会分别输出它们的信息。

也就是说,Promise.race在第一个promise对象变为Fulfilled之后,并不会取消其他promise对象的执行。

在ES6 Promises规范中,也没有取消(中断)promise对象执行的概念,我们必须要确保promise最终进入resolve or reject状态之一。也就是说Promise并不适用于状态可能会固定不变的处理。也有一些类库提供了对promise进行取消的操作。

Promise的实现类库(Library)

由于很多浏览器不支持ES6 Promises,我们需要一些第三方实现的和Promise兼容的类库。

选择Promise类库首先要考虑的是否具有Promises/A+兼容性。

Promises/A+是ES6 Promises的前身,Promise的then也是由社区的规范而来。

这些类库主要有两种:Polyfill和扩展类库

1)Polyfill

  • jakearchibald/es6-promise:应用最广泛的一个库,推荐使用这个库。
  • yahoo/ypromise:这是一个独立版本的 YUI 的 Promise Polyfill。
  • getify/native-promise-only:严格按照ES6 Promises的规范设计,没有添加在规范中没有定义的功能。

2)Promise扩展类库

  • kriskowal/q: Q.promise,这个大家应该都比较熟悉了。Angularjs中的$q也是受此启发。
  • petkaantonov/bluebird:这个类库除了兼容Promise规范之外,还扩展了取消promise对象的运行,取得promise的运行进度,以及错误处理的扩展检测等非常丰富的功能,此外它在实现上还在性能问题下了很大的功夫。

Q等文档里详细介绍了Q的Deferred和jQuery里的Deferred有哪些异同,以及要怎么进行迁移等都进行了详细的说明。

两个有用的附加方法

1)done()

Promise对象的回调链,不管以then方法或catch方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个done方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。

'use strict'
if (typeof Promise.prototype.done === 'undefined') {Promise.prototype.done = function (onFulfilled, onRejected) {this.then(onFulfilled, onRejected).catch(function (error) {setTimeout(function () {throw error}, 0)})}
}// 调用
asyncFunc().then(f1).catch(r1).then(f2).done()

从上面代码可以看到done有以下两个特点。

  • done中出现的错误会被作为异常抛出
  • 终结Promise chain

那么它是如何将异常抛到Promise的外面的呢?其实这里我们利用的是在setTimeout中使用throw方法,直接将异常抛给了外部。

// setTimeout的回调函数中抛出异常
try {setTimeout(function callback () {throw new Error('error')}, 0)
} catch (error) {console.error(error)
}

因为异步的callback中抛出的异常不会被捕获,上面例子中的例外不会被捕获。

ES6 Promises和Promises/A+等在设计上并没有对Promise.prototype.done做出任何规定,但是为什么很多类库都提供了该方法的实现呢?

主要是防止编码时忘记使用catch方法处理异常导致错误排查非常困难的问题。由于Promise的try-catch机制,异常可能会被内部消化掉。这种错误被内部消化的问题也被称为unhandled rejection,从字面上看就是在Rejected时没有找到相应处理的意思。

function JSONPromise (value) {return new Promise(function (resolve) {resolve(JSON.parse(value))})
}// 运行示例
var string = '{}'
JSONPromise(string).then(function (object) {conosle.log(object)
})

在这个例子里,我们错把console拼成了conosle,因此会发生如下错误:

ReferenceError: conosle is not defined

不过在chrome中实测查找这种错误已经相当精准了。所以以前用jQuery的时候用过done,后来在实际项目中并没有使用过done方法。

2)finally()

finally方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与done方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。

Promise.prototype.finally = function (callback) {let P = this.constructorreturn this.then(value  => P.resolve(callback()).then(() => value),reason => P.resolve(callback()).then(() => { throw reason }))
}

这个还是很有用的,我们经常在ajax无论成功还是失败后都要关闭loading。我一般使用这个库promise.prototype.finally。

Promise只能进行异步操作?

var promise = new Promise(function (resolve) {console.log(1) // 1resolve(3)
})promise.then(function(value){console.log(value) // 3
})console.log(2) // 2

执行上面的代码,会依次输出1,2,3。首先new Promise中的函数会立即执行,然后是外面的console.log(2),最后是then回调中的函数。

由于promise.then执行的时候promise对象已经是确定状态,从程序上说对回调函数进行同步调用也是行得通的。

但是即使在调用promise.then注册回调函数的时候promise对象已经是确定的状态,Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针。为什么要这样呢?

这涉及到同步调用和异步调用同时存在导致的混乱。

function onReady (fn) {var readyState = document.readyStateif (readyState === 'interactive' || readyState === 'complete') {fn()} else {window.addEventListener('DOMContentLoaded', fn)}
}onReady(function () {console.log('DOM fully loaded and parsed')
})console.log('==Starting==')

上面的代码如果在调用onReady之前DOM已经载入的话:对回调函数进行同步调用。

如果在调用onReady之前DOM还没有载入的话:通过注册DOMContentLoaded事件监听器来对回调函数进行异步调用。

因此,如果这段代码在源文件中出现的位置不同,在控制台上打印的log消息顺序也会不同。

为了解决这个问题,我们可以选择统一使用异步调用的方式:

function onReady (fn) {var readyState = document.readyStateif (readyState === 'interactive' || readyState === 'complete') {setTimeout(fn, 0)} else {window.addEventListener('DOMContentLoaded', fn)}
}onReady(function () {console.log('DOM fully loaded and parsed')
})console.log('==Starting==')

关于这个问题,在Effective JavaScript的第67项不要对异步回调函数进行同步调用中也有详细介绍:

  • 绝对不能对异步回调函数(即使在数据已经就绪)进行同步调用。
  • 如果对异步回调函数进行同步调用的话,处理顺序可能会与预期不符,可能带来意料之外的后果。
  • 对异步回调函数进行同步调用,还可能导致栈溢出或异常处理错乱等问题。
  • 如果想在将来某时刻调用异步回调函数的话,可以使用setTimeout等异步API。

为了避免上述中同时使用同步、异步调用可能引起的混乱问题,Promise在规范上规定Promise只能使用异步调用方式。

由于Promise保证了每次调用都是以异步方式进行的,所以我们在实际编码中不需要调用setTimeout来自己实现异步调用:

function onReadyPromise () {return new Promise(function (resolve, reject) {var readyState = document.readyStateif (readyState === 'interactive' || readyState === 'complete') {resolve()} else {window.addEventListener('DOMContentLoaded', resolve)}})
}onReadyPromise().then(function () {console.log('DOM fully loaded and parsed')
})console.log('==Starting==')

异步操作顺序问题

前面Promise.resolve()章节的三个promise,我们看看其执行顺序是怎样的?

var p1 = Promise.resolve(1)var p2 = Promise.resolve(p1)var p3 = new Promise(function (resolve, reject) {resolve(p1)
})var p4 = new Promise(function (resolve, reject) {reject(p1)
})p3.then(function (value) {console.log('p3 : ' + value)
})p2.then(function (value) {console.log('p2 : ' + value)
})p4.then(function (value) {console.log('p4-1 : ' + value)
}, function (value) {console.log('p4-1 : ' + value)
})p4.then(function (value) {console.log('p4-2 : ' + value)
}).catch(function (value) {console.log('p4-2 : ' + value)
})p1.then(function (value) {console.log('p1 : ' + value)
})

我们在比较新的浏览器控制台输出会发现顺序为2,4-1,1,4-2,3(测试发现chrome55、56中则是最先打印出3)。这个不知道怎么解释了,为什么p3会最后执行?暂时没找到什么可靠的资料,有大神知道的话,请评论指出。

Promise chain(Promise方法链)

Promise chain流程

function taskA () {console.log('Task A')
}function taskB () {console.log('Task B')
}function onRejected (error) {console.log('Catch Error: A or B', error)
}function finalTask () {console.log('Final Task')
}var promise = Promise.resolve()
promise.then(taskA).then(taskB).catch(onRejected).then(finalTask)

在上述代码中,我们没有为then方法指定第二个参数(onRejected),可以像下面这样来理解:

then:注册onResolved时的回调函数

catch:注册onRejected时的回调函数

1)taskA、taskB都没有发生异常,会按照taskA → taskB → finalTask这个流程来进行处理

2)taskA没有发生异常,taskB发生异常,会按照taskA → taskB → onRejected → finalTask这个流程来进行处理

3)taskA发生异常,会按照taskA → onRejected → finalTask这个流程来进行处理,TaskB是不会被调用的

function taskA () {console.log('Task A')throw new Error('throw Error @ Task A')
}function taskB () {console.log('Task B') // 不会被调用
}function onRejected (error) {console.log(error) // => 'throw Error @ Task A'
}function finalTask () {console.log('Final Task')
}var promise = Promise.resolve()
promise.then(taskA).then(taskB).catch(onRejected).then(finalTask)

在本例中我们在taskA中使用了throw方法故意制造了一个异常。但在实际中想主动进行onRejected调用的时候,应该返回一个Rejected状态的promise对象。

promise chain中如何传递参数?

如果Task A想给Task B传递一个参数该怎么办呢?其实非常简单,只要在taskA中return一个值,这个值会作为参数传递给taskB。

function doubleUp (value) {return value * 2
}function increment (value) {return value + 1
}function output (value) {console.log(value) // => (1 + 1) * 2
}var promise = Promise.resolve(1)
promise.then(increment).then(doubleUp).then(output).catch(function (error) {// promise chain中出现异常的时候会被调用console.error(error)})

每个方法中return的值不仅只局限于字符串或者数值类型,也可以是对象或者promise对象等复杂类型。

return的值会由Promise.resolve(return的返回值)进行相应的包装处理,因此不管回调函数中会返回一个什么样的值,最终then的结果都是返回一个新创建的promise对象。

也就是说,Promise的then方法不仅仅是注册一个回调函数那么简单,它还会将回调函数的返回值进行变换,创建并返回一个promise对象。

如何停止promise chain

在使用Promise处理一些复杂逻辑的过程中,我们有时候会想要在发生某种错误后就停止执行Promise链后面所有的代码。

然而Promise本身并没有提供这样的功能,一个操作,要么成功,要么失败,要么跳转到then里,要么跳转到catch里。

具体怎么做,请查看这篇文章从如何停掉 Promise 链说起。

每次调用then都会返回一个新创建的promise对象

从代码上乍一看,aPromise.then(...).catch(...)像是针对最初的aPromise对象进行了一连串的方法链调用。

然而实际上不管是then还是catch方法调用,都返回了一个新的promise对象。

var aPromise = new Promise(function (resolve) {resolve(100)
})var thenPromise = aPromise.then(function (value) {console.log(value)
})var catchPromise = thenPromise.catch(function (error) {console.error(error)
})console.log(aPromise !== thenPromise) // => true
console.log(thenPromise !== catchPromise) // => true

执行上面代码,证明了then和catch都返回了和调用者不同的promise对象。知道了这点,我们就很容易明白下面两种调用方法的区别:

// 1: 对同一个promise对象同时调用 `then` 方法
var aPromise = new Promise(function (resolve) {resolve(100)
})aPromise.then(function (value) {return value * 2
})aPromise.then(function (value) {return value * 2
})aPromise.then(function (value) {console.log('1: ' + value) // => 100
})// vs// 2: 对 `then` 进行 promise chain 方式进行调用
var bPromise = new Promise(function (resolve) {resolve(100)
})bPromise.then(function (value) {return value * 2
}).then(function (value) {return value * 2
}).then(function (value) {console.log('2: '' + value) // => 100 * 2 * 2
})

下面是一个由方法1中的then用法导致的比较容易出现的很有代表性的反模式的例子:

// then的错误使用方法
function badAsyncCall() {var promise = Promise.resolve()promise.then(function() {// 任意处理return newVar})return promise
}

这种写法有很多问题,首先在promise.then中产生的异常不会被外部捕获,此外,也不能得到then的返回值,即使其有返回值。

不仅then和catch都返回了和调用者不同的promise对象,Promise.all和Promise.race,他们都会接收一组promise对象为参数,并返回一个和接收参数不同的、新的promise对象。

使用then的第二个参数还是catch处理异常?

之前我们说过 .catch也可以理解为promise.then(undefined, onRejected)。那么使用这两种方法进行错误处理有什么区别呢?

function throwError (value) {// 抛出异常throw new Error(value)
}// <1> onRejected不会被调用
function badMain (onRejected) {return Promise.resolve(42).then(throwError, onRejected)
}// <2> 有异常发生时onRejected会被调用
function goodMain (onRejected) {return Promise.resolve(42).then(throwError).catch(onRejected)
}// 运行示例
badMain(function () {console.log("BAD")
})goodMain(function () {console.log("GOOD")
})

在上面的代码中,badMain是一个不太好的实现方式(但也不是说它有多坏),goodMain则是一个能非常好的进行错误处理的版本。

为什么说badMain不好呢?,因为虽然我们在.then的第二个参数中指定了用来错误处理的函数,但实际上它却不能捕获第一个参数onResolved指定的函数(本例为 throwError)里面出现的错误。

也就是说,这时候即使throwError抛出了异常,onRejected指定的函数也不会被调用(即不会输出"BAD"字样)。

与此相对的是,goodMain的代码则遵循了throwError → onRejected的调用流程。这时候throwError中出现异常的话,在会被方法链中的下一个方法,即.catch所捕获,进行相应的错误处理。

.then方法中的onRejected参数所指定的回调函数,实际上针对的是其promise对象或者之前的promise对象,而不是针对.then方法里面指定的第一个参数,即onResolved所指向的对象,这也是then和catch表现不同的原因。

1)使用promise.then(onResolved, onRejected)的话

在onResolved中发生异常的话,在onRejected中是捕获不到这个异常的。

2)在promise.then(onResolved).catch(onRejected)的情况下

then中产生的异常能在.catch中捕获

3).then和.catch在本质上是没有区别的

需要分场合使用。

我们需要注意如果代码类似badMain那样的话,就可能出现程序不会按预期运行的情况,从而不能正确的进行错误处理。

IE8及IE8以下catch兼容问题

IE8及IE8以下即使已经引入了Promise的polyfill,使用catch方法仍然会出现identifier not found的语法错误。

这是怎么回事呢?实际上这和catch是ECMAScript的保留字(Reserved Word)有关。

在ECMAScript 3中保留字是不能作为对象的属性名使用的。而IE8及以下版本都是基于ECMAScript 3实现的,因此不能将catch作为属性来使用,也就不能编写类似promise.catch()的代码,因此就出现了identifier not found这种语法错误了。

而现代浏览器都支持ECMAScript 5,而在ECMAScript 5中保留字都属于IdentifierName,也可以作为属性名使用了。

点标记法(dot notation)要求对象的属性必须是有效的标识符(在ECMAScript 3中则不能使用保留字)。

但是使用中括号标记法(bracket notation)的话,则可以将非合法标识符作为对象的属性名使用。

var promise = Promise.reject(new Error('message'))
promise['catch'](function (error) {console.error(error)
})

由于catch标识符可能会导致问题出现,因此一些类库(Library)也采用了caught作为函数名,而函数要完成的工作是一样的。

而且很多压缩工具自带了将promise.catch转换为promise['catch']的功能,所以可能不经意之间也能帮我们解决这个问题。

使用reject而不是throw

var promise = new Promise(function (resolve, reject) {throw new Error("message")
})promise.catch(function (error) {console.error(error) // => "message"
})

上面代码其实并没有什么问题,但是有两个不好的地方:

首先是因为我们很难区分throw是我们主动抛出来的,还是因为真正的其它异常导致的。

其次本来这是和调试没有关系的地方,throw时就会触发调试器的break行为,会干扰浏览器的调试器中break的功能的正常使用。

所以使用reject会比使用throw安全。

再议Promise.resolve和Thenable

之前我们已经讲过Promise.resolve能将thenable对象转化为promise对象。接下来我们再看看将thenable对象转换为promise对象这个功能都能具体做些什么事情。

以Web Notification为例,普通使用回调函数方式如下:

function notifyMessage (message, options, callback) {if (Notification && Notification.permission === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else if (Notification.requestPermission) {Notification.requestPermission(function (status) {if (Notification.permission !== status) {Notification.permission = status}if (status === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else {callback(new Error('user denied'))}})} else {callback(new Error('doesn\'t support Notification API'))}
}// 运行实例
// 第二个参数是传给 `Notification` 的option对象
notifyMessage('Hi!', {}, function (error, notification) {if (error) {return console.error(error)}console.log(notification) // 通知对象
})

使用Promise改写回调:

function notifyMessage (message, options, callback) {if (Notification && Notification.permission === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else if (Notification.requestPermission) {Notification.requestPermission(function (status) {if (Notification.permission !== status) {Notification.permission = status}if (status === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else {callback(new Error('user denied'))}})} else {callback(new Error('doesn\'t support Notification API'))}
}function notifyMessageAsPromise (message, options) {return new Promise(function (resolve, reject) {notifyMessage(message, options, function (error, notification) {if (error) {reject(error)} else {resolve(notification)}})})
}// 运行示例
notifyMessageAsPromise('Hi!').then(function (notification) {console.log(notification) // 通知对象
}).catch(function(error){console.error(error)
})

使用thenable对象形式:

function notifyMessage (message, options, callback) {if (Notification && Notification.permission === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else if (Notification.requestPermission) {Notification.requestPermission(function (status) {if (Notification.permission !== status) {Notification.permission = status}if (status === 'granted') {var notification = new Notification(message, options)callback(null, notification)} else {callback(new Error('user denied'))}})} else {callback(new Error('doesn\'t support Notification API'))}
}// 返回 `thenable`
function notifyMessageAsThenable (message, options) {return {'then': function (resolve, reject) {notifyMessage(message, options, function (error, notification) {if (error) {reject(error)} else {resolve(notification)}})}}
}// 运行示例
Promise.resolve(notifyMessageAsThenable('message')).then(function (notification) {console.log(notification) // 通知对象
}).catch(function (error) {console.error(error)
})

Thenable风格表现为位于回调和Promise风格中间的一种状态,不用考虑Promise的兼容问题。一般不作为类库的公开API,更多情况下是在内部使用Thenable。Thenable对象更多的是用来在Promise类库之间进行相互转换。

使用thenable将promise对象转换为Q promise对象:

var Q = require('Q')// 这是一个ES6的promise对象
var promise = new Promise(function (resolve) {resolve(1)
})// 变换为Q promise对象
Q(promise).then(function (value) {console.log(value)
}).finally(function () { // Q promise对象可以使用finally方法console.log('finally')
})

Deferred和Promise

Deferred和Promise不同,它没有共通的规范,每个Library都是根据自己的喜好来实现的。

在这里,我们打算以jQuery.Deferred类似的实现为中心进行介绍。

简单来说,Deferred和Promise具有如下的关系。

  • Deferred拥有Promis(当然也有的Deferred实现并没有内涵Promise)
  • Deferred具备对Promise的状态进行操作的特权方法

用Deferred实现的getURL(Deferred基于promise实现):

function Deferred () {this.promise = new Promise(function (resolve, reject) {this._resolve = resolvethis._reject = reject}.bind(this))
}Deferred.prototype.resolve = function (value) {this._resolve.call(this.promise, value)
}Deferred.prototype.reject = function (reason) {this._reject.call(this.promise, reason)
}function getURL (URL) {var deferred = new Deferred()var req = new XMLHttpRequest()req.open('GET', URL, true)req.onload = function () {if (req.status === 200) {deferred.resolve(req.responseText)} else {deferred.reject(new Error(req.statusText))}}req.onerror = function () {deferred.reject(new Error(req.statusText))}req.send()return deferred.promise
}// 运行示例
var URL = 'http://httpbin.org/get'
getURL(URL).then(function onFulfilled (value){console.log(value)
}).catch(console.error.bind(console))

Promise实现的getURL:

function getURL (URL) {return new Promise(function (resolve, reject) {var req = new XMLHttpRequest()req.open('GET', URL, true)req.onload = function () {if (req.status === 200) {resolve(req.responseText)} else {reject(new Error(req.statusText))}}req.onerror = function () {reject(new Error(req.statusText))}req.send()})
}// 运行示例
var URL = 'http://httpbin.org/get'
getURL(URL).then(function onFulfilled (value){console.log(value)
}).catch(console.error.bind(console))

对比上述两个版本的getURL ,我们发现它们有如下不同。

  • Deferred的话不需要将代码用Promise括起来,由于没有被嵌套在函数中,可以减少一层缩进。
  • 反过来没有Promise里的错误处理逻辑。

在以下方面,它们则完成了同样的工作。

  • 整体处理流程,调用resolve、reject的时机。
  • 函数都返回了promise对象。

由于Deferred包含了Promise,所以大体的流程还是差不多的,不过Deferred有对Promise进行操作的特权方法,以及可以对流程控制进行自由定制。

上面我们只是简单的实现了一个Deferred ,我想你已经看到了它和Promise之间的差异了吧。

如果说Promise是用来对值进行抽象的话,Deferred则是对处理还没有结束的状态或操作进行抽象化的对象,我们也可以从这一层的区别来理解一下这两者之间的差异。

换句话说,Promise代表了一个对象,这个对象的状态现在还不确定,但是未来一个时间点它的状态要么变为正常值(FulFilled),要么变为异常值(Rejected);而Deferred对象表示了一个处理还没有结束的这种事实,在它的处理结束的时候,可以通过Promise来取得处理结果。

使用Promise.race和delay取消XHR请求

XHR有一个timeout属性,使用该属性也可以简单实现超时功能,但是为了能支持多个XHR同时超时或者其他功能,我们采用了容易理解的异步方式在XHR中通过超时来实现取消正在进行中的操作。

1)让Promise等待指定时间

function delayPromise (ms) {return new Promise(function (resolve) {setTimeout(resolve, ms)})
}delayPromise(100).then(function () {alert('已经过了100ms!')
})

2) 使用promise.race()来实现超时promise:

function timeoutPromise (promise, ms) {var timeout = delayPromise(ms).then(function () {throw new Error('Operation timed out after ' + ms + ' ms')})return Promise.race([promise, timeout])
}

上面代码promise的状态改变的时间超过了ms就会throw Error。

// 运行示例
var taskPromise = new Promise(function(resolve){// 随便一些什么处理var delay = Math.random() * 2000setTimeout(function() {resolve(delay + 'ms')}, delay)
})timeoutPromise(taskPromise, 1000).then(function (value) {console.log('taskPromise在规定时间内结束 : ' + value)
}).catch(function (error) {console.log('发生超时', error)
})

3)定制Error对象

为了能区分这个Error对象的类型,我们再来定义一个Error对象的子类TimeoutError。

function copyOwnFrom (target, source) {Object.getOwnPropertyNames(source).forEach(function (propName) {Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName))})return target
}function TimeoutError () {var superInstance = Error.apply(null, arguments)copyOwnFrom(this, superInstance)
}TimeoutError.prototype = Object.create(Error.prototype)
TimeoutError.prototype.constructor = TimeoutError

它的使用方法和普通的Error对象一样,使用throw语句即可

var promise = new Promise(function () {throw new TimeoutError('timeout')
})promise.catch(function (error) {console.log(error instanceof TimeoutError) // true
})

有了这个TimeoutError对象,我们就能很容易区分捕获的到底是因为超时而导致的错误,还是其他原因导致的Error对象了。

4)通过超时取消XHR操作

取消XHR操作本身的话并不难,只需要调用XMLHttpRequest对象的abort()方法就可以了。

为了能在外部调用abort()方法,我们先对之前本节出现的getURL进行简单的扩展,cancelableXHR方法除了返回一个包装了XHR的promise对象之外,还返回了一个用于取消该XHR请求的abort方法。

function copyOwnFrom (target, source) {Object.getOwnPropertyNames(source).forEach(function (propName) {Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName))})return target
}function TimeoutError () {var superInstance = Error.apply(null, arguments)copyOwnFrom(this, superInstance)
}TimeoutError.prototype = Object.create(Error.prototype)
TimeoutError.prototype.constructor = TimeoutErrorfunction delayPromise (ms) {return new Promise(function (resolve) {setTimeout(resolve, ms)})
}function timeoutPromise(promise, ms) {var timeout = delayPromise(ms).then(function () {return Promise.reject(new TimeoutError('Operation timed out after ' + ms + ' ms'))})return Promise.race([promise, timeout])
}function cancelableXHR(URL) {var req = new XMLHttpRequest()var promise = new Promise(function (resolve, reject) {req.open('GET', URL, true)req.onload = function () {if (req.status === 200) {resolve(req.responseText)} else {reject(new Error(req.statusText))}}req.onerror = function () {reject(new Error(req.statusText))}req.onabort = function () {reject(new Error('abort this request'))}req.send()})var abort = function () {// 如果request还没有结束的话就执行abort// https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequestif (req.readyState !== XMLHttpRequest.UNSENT) {req.abort()}}return {promise: promise,abort: abort}
}var object = cancelableXHR('http://httpbin.org/get')// main
timeoutPromise(object.promise, 1000).then(function (contents) {console.log('Contents', contents)
}).catch(function (error) {if (error instanceof TimeoutError) {object.abort()return console.log(error)}console.log('XHR Error :', error)
})

5)代码分割优化处理

在前面的cancelableXHR中,promise对象及其操作方法都是在一个对象中返回的,看起来稍微有些不太好理解。

从代码组织的角度来说一个函数只返回一个值(promise对象)是一个非常好的习惯,但是由于在外面不能访问cancelableXHR方法中创建的req变量,所以我们需要编写一个专门的函数(上面的例子中的abort)来对这些内部对象进行处理。

当然也可以考虑到对返回的promise对象进行扩展,使其支持abort方法,但是由于promise对象是对值进行抽象化的对象,如果不加限制的增加操作用的方法的话,会使整体变得非常复杂。

大家都知道一个函数做太多的工作都不认为是一个好的习惯,因此我们不会让一个函数完成所有功能,也许像下面这样对函数进行分割是一个不错的选择。

  • 返回包含XHR的promise对象
  • 接收promise对象作为参数并取消该对象中的XHR请求

将这些处理整理为一个模块的话,以后扩展起来也方便,一个函数所做的工作也会比较精炼,代码也会更容易阅读和维护。

使用common.js规范来写cancelableXHR.js:

'use strict'
var requestMap = {}function createXHRPromise (URL) {var req = new XMLHttpRequest()var promise = new Promise(function (resolve, reject) {req.open('GET', URL, true)req.onreadystatechange = function () {if (req.readyState === XMLHttpRequest.DONE) {delete requestMap[URL]}}req.onload = function () {if (req.status === 200) {resolve(req.responseText)} else {reject(new Error(req.statusText))}}req.onerror = function () {reject(new Error(req.statusText))}req.onabort = function () {reject(new Error('abort this req'))}req.send()})requestMap[URL] = {promise: promise,request: req}return promise
}function abortPromise (promise) {if (typeof promise === 'undefined') {return}var requestObject.keys(requestMap).some(function (URL) {if (requestMap[URL].promise === promise) {request = requestMap[URL].requestreturn true}})if (request != null && request.readyState !== XMLHttpRequest.UNSENT) {request.abort()}
}module.exports.createXHRPromise = createXHRPromise
module.exports.abortPromise = abortPromise

调用:

var cancelableXHR = require('./cancelableXHR')var xhrPromise = cancelableXHR.createXHRPromise('http://httpbin.org/get') // 创建包装了XHR的promise对象xhrPromise.catch(function (error) {// 调用 abort 抛出的错误
})cancelableXHR.abortPromise(xhrPromise) //   取消在创建的promise对象的请求操作

promise串行处理

Promise.all()可以进行promise对象的并行处理,那么怎么实现串行处理呢?

我们将处理内容统一放到数组里,再配合for循环进行处理:

var request = {comment: function getComment () {return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse)},people: function getPeople () {return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse)}
}function main() {function recordValue(results, value) {results.push(value)return results}// [] 用来保存初始化值var pushValue = recordValue.bind(null, [])// 返回promise对象的函数的数组var tasks = [request.comment, request.people]var promise = Promise.resolve()// 开始的地方for (var i = 0; i < tasks.length; i++) {var task = tasks[i]promise = promise.then(task).then(pushValue)}return promise
}// 运行示例
main().then(function (value) {console.log(value)
}).catch(function (error) {console.error(error)
})

上面代码中的promise = promise.then(task).then(pushValue)通过不断对promise进行处理,不断的覆盖promise变量的值,以达到对promise对象的累积处理效果。

但是这种方法需要promise这个临时变量,从代码质量上来说显得不那么简洁。我们可以使用Array.prototype.reduce来优化main函数:

function main() {function recordValue (results, value) {results.push(value)return results}var pushValue = recordValue.bind(null, [])var tasks = [request.comment, request.people]return tasks.reduce(function (promise, task) {return promise.then(task).then(pushValue)}, Promise.resolve())
}

实际上我们可以提炼出进行顺序处理的函数:

function sequenceTasks(tasks) {function recordValue(results, value) {results.push(value)return results}var pushValue = recordValue.bind(null, [])return tasks.reduce(function (promise, task) {return promise.then(task).then(pushValue)}, Promise.resolve())
}

这样我们只要如下调用,代码也更加清晰易懂了:

var request = {comment: function getComment() {return getURL('http://azu.github.io/promises-book/json/comment.json').then(JSON.parse)},people: function getPeople() {return getURL('http://azu.github.io/promises-book/json/people.json').then(JSON.parse)}
}function main() {return sequenceTasks([request.comment, request.people])
}// 运行示例
main().then(function (value) {console.log(value)
}).catch(function (error) {console.error(error)
})

同时请求按序处理

下面的内容来自google开发社区的一篇关于promise的文章JavaScript Promise:简介

假设我们要根据story.json通过ajax获取章节内容,每一次ajax只能获取一节内容。那么怎么做到又快又能按序展示章节内容呢?即如果第一章下载完后,我们可将其添加到页面。这可让用户在其他章节下载完毕前先开始阅读。如果第三章比第二章先下载完后,我们不将其添加到页面,因为还缺少第二章。第二章下载完后,我们可添加第二章和第三章,后面章节也是如此添加。

前一节的串行方法只能一个ajax请求task处理完后再去执行下一个task,而Promise.all()能同时请求,但是只有全部请求结束后才能得到有序的数组。

具体实现请看下面实例。

我们可以使用JSON来同时获取所有章节,然后创建一个向文档中添加章节的顺序。

story.json如下:

{"heading": "<h1>A story about something</h1>","chapterUrls": ["chapter-1.json","chapter-2.json","chapter-3.json","chapter-4.json","chapter-5.json"]
}

具体处理代码:

function getJson(url) {return get(url).then(JSON.parse)
}getJSON('story.json').then(function (story) {addHtmlToPage(story.heading) // 文章头部添加到页面// 将拿到的chapterUrls数组map为json promises数组,这样可以保证并行下载return story.chapterUrls.map(getJSON).reduce(function(sequence, chapterPromise) {// 用reduce方法链式调用promises,并将每个章节的内容到添加页面return sequence.then(function () {// 等待获取当前准备插入页面的顺序的资源,然后等待这个顺序对应章节的成功请求// Wait for everything in the sequence so far, then wait for this chapter to arrive.return chapterPromise}).then(function(chapter) {addHtmlToPage(chapter.html) // 将章节内容到添加页面})}, Promise.resolve())}).then(function() {addTextToPage('All done') // 页面添加All done文字}).catch(function(err) {// catch错误信息addTextToPage('Argh, broken: '' + err.message)}).then(function() {document.querySelector('.spinner').style.display = 'none' // 关闭加载提示})

Promise和链式调用

在Promise中你可以将then和catch等方法连在一起写。这非常像DOM或者jQuery中的链式调用。

一般的方法链都通过返回this将多个方法串联起来。

那么怎么在不改变已有采用了方法链编写的代码的外部接口的前提下,如何在内部使用Promise进行重写呢?

1)fs中的方法链

以Node.js中的fs为例。

此外,这里的例子我们更重视代码的易理解性,因此从实际上来说这个例子可能并不算太实用。

有fs-method-chain.js:

'use strict'
var fs = require('fs')function File() {this.lastValue = null
}// Static method for File.prototype.read
File.read = function FileRead(filePath) {var file = new File()return file.read(filePath)
}File.prototype.read = function (filePath) {this.lastValue = fs.readFileSync(filePath, 'utf-8')return this
}File.prototype.transform = function (fn) {this.lastValue = fn.call(this, this.lastValue)return this
}File.prototype.write = function (filePath) {this.lastValue = fs.writeFileSync(filePath, this.lastValue)return this
}module.exports = File

调用:

var File = require('./fs-method-chain')
var inputFilePath = 'input.txt',outputFilePath = 'output.txt'
File.read(inputFilePath).transform(function (content) {return '>>' + content}).write(outputFilePath)

2)基于Promise的fs方法链

下面我们就在不改变刚才的方法链对外接口的前提下,采用Promise对内部实现进行重写。

'use strict'
var fs = require('fs')function File() {this.promise = Promise.resolve()
}// Static method for File.prototype.read
File.read = function (filePath) {var file = new File()return file.read(filePath)
}File.prototype.then = function (onFulfilled, onRejected) {this.promise = this.promise.then(onFulfilled, onRejected)return this
}File.prototype['catch'] = function (onRejected) {this.promise = this.promise.catch(onRejected)return this
}File.prototype.read = function (filePath) {return this.then(function () {return fs.readFileSync(filePath, 'utf-8')})
}File.prototype.transform = function (fn) {return this.then(fn)
}File.prototype.write = function (filePath) {return this.then(function (data) {return fs.writeFileSync(filePath, data)})
}module.exports = File

3)两者的区别

要说fs-method-chain.js和Promise版两者之间的差别,最大的不同那就要算是同步和异步了。

如果在类似fs-method-chain.js的方法链中加入队列等处理的话,就可以实现几乎和异步方法链同样的功能,但是实现将会变得非常复杂,所以我们选择了简单的同步方法链。

Promise版的话如同之前章节所说只会进行异步操作,因此使用了promise的方法链也是异步的。

另外两者的错误处理方式也是不一致的。

虽然fs-method-chain.js里面并不包含错误处理的逻辑,但是由于是同步操作,因此可以将整段代码用try-catch包起来。

在Promise版提供了指向内部promise对象的then和catch别名,所以我们可以像其它promise对象一样使用catch来进行错误处理。

如果你想在fs-method-chain.js中自己实现异步处理的话,错误处理可能会成为比较大的问题;可以说在进行异步处理的时候,还是使用Promise实现起来比较简单。

4)Promise之外的异步处理

如果你很熟悉Node.js的話,那么看到方法链的话,你是不是会想起来Stream呢。

如果使用Stream的话,就可以免去了保存this.lastValue的麻烦,还能改善处理大文件时候的性能。 另外,使用Stream的话可能会比使用Promise在处理速度上会快些。

因此,在异步处理的时候并不是说Promise永远都是最好的选择,要根据自己的目的和实际情况选择合适的实现方式。

5)Promise wrapper

再回到fs-method-chain.js和Promise版,这两种方法相比较内部实现也非常相近,让人觉得是不是同步版本的代码可以直接就当做异步方式来使用呢?

由于JavaScript可以向对象动态添加方法,所以从理论上来说应该可以从非Promise版自动生成Promise版的代码。(当然静态定义的实现方式容易处理)

尽管ES6 Promises并没有提供此功能,但是著名的第三方Promise实现类库bluebird等提供了被称为Promisification的功能。

如果使用类似这样的类库,那么就可以动态给对象增加promise版的方法。

var fs = Promise.promisifyAll(require('fs'))fs.readFileAsync('myfile.js', 'utf8').then(function (contents) {console.log(contents)
}).catch(function (e) {console.error(e.stack)
})

前面的Promisification具体都干了些什么光凭想象恐怕不太容易理解,我们可以通过给原生 Array增加Promise版的方法为例来进行说明。

在JavaScript中原生DOM或String等也提供了很多创建方法链的功能。Array中就有诸如map和filter等方法,这些方法会返回一个数组类型,可以用这些方法方便的组建方法链。

'use strict'function ArrayAsPromise (array) {this.array = arraythis.promise = Promise.resolve()
}ArrayAsPromise.prototype.then = function (onFulfilled, onRejected) {this.promise = this.promise.then(onFulfilled, onRejected)return this
}ArrayAsPromise.prototype['catch'] = function (onRejected) {this.promise = this.promise.catch(onRejected)return this
}Object.getOwnPropertyNames(Array.prototype).forEach(function (methodName) {// Don't overwriteif (typeof ArrayAsPromise[methodName] !== 'undefined') {return}var arrayMethod = Array.prototype[methodName]if (typeof  arrayMethod !== 'function') {return}ArrayAsPromise.prototype[methodName] = function () {var that = thisvar args = argumentsthis.promise = this.promise.then(function () {that.array = Array.prototype[methodName].apply(that.array, args)return that.array})return this}
})module.exports = ArrayAsPromise
module.exports.array = function newArrayAsPromise (array) {return new ArrayAsPromise(array)
}

原生的Array和ArrayAsPromise在使用时有什么差异呢?我们可以通过对上面的代码进行测试来了解它们之间的不同点。

'use strict'
var assert = require('power-assert')
var ArrayAsPromise = require('../src/promise-chain/array-promise-chain')describe('array-promise-chain', function () {function isEven(value) {return value % 2 === 0}function double(value) {return value * 2}beforeEach(function () {this.array = [1, 2, 3, 4, 5]})describe('Native array', function () {it('can method chain', function () {var result = this.array.filter(isEven).map(double)assert.deepEqual(result, [4, 8])})})describe('ArrayAsPromise', function () {it('can promise chain', function (done) {var array = new ArrayAsPromise(this.array)array.filter(isEven).map(double).then(function (value) {assert.deepEqual(value, [4, 8])}).then(done, done)})})
})

我们看到,在ArrayAsPromise中也能使用Array的方法。原生的Array是同步处理,而ArrayAsPromise则是异步处理。

仔细看一下ArrayAsPromise的实现,也许你已经注意到了,Array.prototype的所有方法都被实现了。但是,Array.prototype中也存在着类似array.indexOf等并不会返回数组类型数据的方法,这些方法如果也要支持链式调用的话就有些不自然了。

在这里非常重要的一点是,我们可以通过这种方式,为具有接收相同类型数据接口的API动态的创建Promise版的API。如果我们能意识到这种API的规则性的话,那么就可能发现一些新的使用方法。

自己实现一个Promise类

剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类

Promise反面模式(anti-pattern)

关于反面模式,维基百科是这样定义的:在软件工程中,一个反面模式(anti-pattern或antipattern)指的是在实践中明显出现但又低效或是有待优化的设计模式,是用来解决问题的带有共同性的不良方法。

Promise中常见的反面模式有嵌套的promise、没有正确error handle等。

Promise反面模式原文

Promise反面模式中文翻译

Promise常见错误

We have a problem with promises原文

We have a problem with promises中文翻译

其他强大的异步处理方式

1)使用async/await

async/await更加强大,能写出更像同步的代码。但是基础仍然是要掌握Promise。

2)使用Rxjs(Angular2后框架自带)。

参考

JavaScript Promise迷你书(中文版)

阮一峰 ECMAScript6入门 Promise对象

JavaScript Promise:简介

转载于:https://www.cnblogs.com/ang-/p/7565321.html

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 【JAVA IO流之转换流OutputStreamWriter和InputStreamReader】

    java字符流的底层还是使用字节流实现的,所以字节流时刻转换为字符流的。转换流主要使用到OutputStreamWriter:将字节输出流转换为字符输出流形式InputStreamReader:将输入的字节流转换为字符流输入形式1.OutputStreamWriter(字节输出流-->字符输出流)OutputStreamWrite…...

    2024/5/2 18:57:32
  2. React.js入门笔记

    # React.js入门笔记 核心提示 这是本人学习react.js的第一篇入门笔记&#xff0c;估计也会是该系列涵盖内容最多的笔记&#xff0c;主要内容来自英文官方文档的快速上手部分和阮一峰博客教程。当然&#xff0c;还有我自己尝试的实例。日后还将对官方文档进阶和高级部分分专题进…...

    2024/5/10 14:41:15
  3. 你不知道的XHR

    你不知道的 XMLHttpRequest 原文地址&#xff1a; https://juejin.im/post/58e4a174ac502e006c1e18f4 本文详细介绍了 XMLHttpRequest 相关知识&#xff0c;涉及内容&#xff1a; AJAX、XMLHTTP、XMLHttpRequest详解、XMLHttpRequest Level 1、Level 2 详解XHR 上传、下载数据、…...

    2024/5/2 11:52:33
  4. 你不知道的 XMLHttpRequest

    本文详细介绍了 XMLHttpRequest 相关知识&#xff0c;涉及内容&#xff1a; AJAX、XMLHTTP、XMLHttpRequest详解、XMLHttpRequest Level 1、Level 2 详解XHR 上传、下载数据、XHR 流式传输、XHR 定时轮询和长轮询区别与优缺点、XMLHttpRequest 库 (Mock.js、Zone.js、Oboe.js、…...

    2024/5/2 19:24:58
  5. 各类工具和技术博客

    文章目录[隐藏]前端记录Join GitHub todayGitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up Find fileCopy pathfe-record/ README.md c3498b0on 30 Martumarsupdate 1 contrib…...

    2024/5/5 21:40:18
  6. react入门教程记录

    公司业务原因需要使用react 目前大火的双向绑定框架有react&#xff0c;vue&#xff0c;backbone&#xff0c;angular等 这类框架有两个最大的亮点&#xff0c;1是数据绑定功能&#xff0c;2是组件复用技术 看了一边react&#xff0c;忽然就明白了为什么vue可以迅速的崛起。…...

    2024/5/5 18:08:01
  7. Github前端项目排名

    Github前端项目排名(2016-04-04) 一、前言 近几年前端技术日新月异&#xff0c;从 RequireJS 到 AngularJS 再到 React&#xff0c;似乎每天都有新的技术诞生。而大神们总能第一时间获取到最新资讯&#xff0c;在我们还在刀耕火种的年代就使用上各种高新技术&#xff0c;甚是膜…...

    2024/5/6 1:59:47
  8. 前端知识点

    原文链接&#xff1a;http://caibaojian.com/fe-knowledge-points.html 前端知识点 A-A前端博客•2015-12-24•前端开发资源•前端开发•1650View1文章目录 Pre IntroductionEditorToolsCommmunityTask 1 HTML\CSS\Git Basic Reading:GitHTML:CSS & Layout:Task 2 JavaScri…...

    2024/5/8 4:30:36
  9. Ajax 知识体系大梳理

    这是一篇万字长文, 系统梳理了ajax相关的知识体系, 几乎囊括了所有ajax的知识点. 原文: louiszhai.github.io/2016/11/02/… 导读 Ajax 全称 Asynchronous JavaScript and XML, 即异步JS与XML. 它最早在IE5中被使用, 然后由Mozilla, Apple, Google推广开来. 典型的代表应用有 O…...

    2024/5/5 4:30:00
  10. Ajax知识体系大梳理

    导读 Ajax 全称 Asynchronous JavaScript and XML, 即异步JS与XML. 它最早在IE5中被使用, 然后由Mozilla, Apple, Google推广开来. 典型的代表应用有 Outlook Web Access, 以及 GMail. 现代网页中几乎无ajax不欢. 前后端分离也正是建立在ajax异步通信的基础之上. 浏览器为ajax做…...

    2024/5/5 20:42:19
  11. Vue CLI 3结合Lerna进行UI框架设计

    第一次在掘金发文章&#xff0c;有点啰里啰嗦&#xff0c;大家见谅。 当前大部分UI框架设计的Webpack配置都相对复杂&#xff0c;例如Element、Ant Design Vue和Muse-UI等Vue组件库。例如Element&#xff0c;为了实现业务层面的两种引入形式&#xff08;完整引入和按需引入&…...

    2024/5/5 23:28:35
  12. ES6学习笔记19:Generator 函数的语法

    文章目录基本定义yield表达式yield表达式和return语句注意与Iterator 接口的关系next方法的参数for...of 循环Generator.prototype.throw()Generator.prototype.return()next()、throw()、return()的共同点yield* 表达式作为对象属性的Generator函数Generator 函数的this使Gene…...

    2024/5/5 16:55:42
  13. AngularJS分页插件的使用

    最终效果&#xff1a; 先查全部 1.后端 我们需要建立一个实体类&#xff0c;PageResult.java&#xff08;加上get,set方法和构造方法&#xff0c;实现序列化&#xff09; 定义 总页数和行数 package entity;import java.io.Serializable; import java.util.List;/*** 分页总…...

    2024/5/6 0:05:37
  14. angular.js+bootstrap 自己做分页插件(三)带数据加载,数据筛选的分页插件

    如图&#xff1a;之前的版本&#xff0c;分页存在一些bug,修改每页分页条数小于5的时候&#xff0c;页面条会有问题&#xff0c;现在已经修复&#xff0c;发布最新的完整的成果。 html代码&#xff1a; <p>每页显示<input type"text" ng-model"pagenum…...

    2024/5/5 20:14:54
  15. Angular2-PrimeNG 分页模块源码学习

    Angular2 PrimeNG源码学习 Paginator分页组件 GITHUB地址 首先分析一下分页功能的需求&#xff1a; 由父组件传入数据总数量&#xff0c;每页显示数量&#xff0c;可自定义初始页由父组件传入分页按钮个数有第一页&#xff0c;上一页&#xff0c;下一页&#xff0c;最后一页…...

    2024/5/5 3:09:13
  16. angular 分页使用方法

    调用方法: var GetAllEmployee function () {//alert($scope.paginationConf.currentPage);var topAllbadsmell {url:defectManager/list,data:{"projectId":parseInt(localStorage.projectId),"farmlandId":0,"jobId":parseInt(localStorage.…...

    2024/5/5 11:40:03
  17. Angular4 自制分页控件

    过年后第一波&#xff0c;自制的分页控件&#xff0c;可能功能没有 PrimeNG 那么好&#xff0c;但是基本可以实现自定义翻页功能&#xff0c;包括&#xff1a;首页/最后一页/上一页/下一页。 用户可以自定义&#xff1a; 1. 当前默认页码&#xff08;如未提供&#xff0c;默认为…...

    2024/5/5 12:11:12
  18. Angular 7+boostrap 分页

    参数定义taskLists: any&#xff1b;//initDatataskPageList: any; // paginationDatapageNo 1; // first pagepageSize 12; // every page countcurPage 1; //currentPagepageCount: any ;代码&#xff1a;ngOnInit(){this.pageCount [1];//这个是为了进这个页面的时候默认…...

    2024/5/5 2:23:58
  19. angular分页的实现

    做数据处理经常要用到分页&#xff0c;ui-bootstrap的分页挺好用的&#xff0c;但是要知道每个属性的意思&#xff0c;之前就在items-per-page上入了坑&#xff0c;ui-bootstrap默认是每页显示10条数据&#xff0c;如果这个值不设置和你想显示的数据条数相同的话&#xff0c;总…...

    2024/5/5 10:49:28
  20. Angular2自定义分页组件

    在项目中&#xff0c;前端传给后台的参数有&#xff1a; pageSize&#xff1a;每页的条数 pageNo&#xff1a;当前页码 比如当前是第1页&#xff0c;每页20条&#xff0c;则后台返回第1页的20条记录&#xff08;sql应该是用limit去获取分页数据&#xff09; 同时&#xff0…...

    2024/5/5 22:47:08

最新文章

  1. Nginx - 配置文件结构(一)

    安装Nginx 以 Ubuntu 为例&#xff0c;安装命令为 sudo apt install nginx常用指令 # 检查配置文件是否有问题 nginx -t# 热加载配置文件 nginx -s reload# 等待处理完当前请求并退出 nginx -s quit# 快速退出 nginx -s stop目录结构 nginx 默认安装位置一般在 /etc/nginx …...

    2024/5/10 19:34:51
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/9 21:23:04
  3. 【GIS学习笔记】polygon和multipolygon的转换

    写得比较全的polygon和multipolygon的介绍&#xff1a; https://www.cnblogs.com/billygisboy/p/17415573.html 从文本文件上看二者的区别就是"type":"Polygon"和"type":"MultiPolygon"&#xff0c;以及MultiPolygon多一层[]或()。 …...

    2024/5/10 2:30:19
  4. llama.cpp运行qwen0.5B

    编译llama.cp 参考 下载模型 05b模型下载 转化模型 创建虚拟环境 conda create --prefixD:\miniconda3\envs\llamacpp python3.10 conda activate D:\miniconda3\envs\llamacpp安装所需要的包 cd G:\Cpp\llama.cpp-master pip install -r requirements.txt python conver…...

    2024/5/10 0:17:58
  5. [网鼎杯 2020 朱雀组]Nmap1

    打开题目 在源代码中看到了提示 先随便输入127.0.0.1 那我们试试输入 127.0.0.1 | ls 可以看到 | 被转义符号\所转义 那我们输入 127.0.0.1 /| ls 得到三条反斜线 我们猜测&#xff0c;我们输入的东西是被escapeshellarg和escapeshellcmd处理过后的结果 我们输入的东西必须…...

    2024/5/9 23:56:43
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/10 12:36:12
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/9 15:10:32
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/9 4:20:59
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/8 20:48:49
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/8 19:33:07
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/8 20:38:49
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/10 10:22:18
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/9 17:11:10
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57