krisguoqin / blog Goto Github PK
View Code? Open in Web Editor NEW博客,记录知识点与问题
博客,记录知识点与问题
git commit 之后发现提交错了,如果想撤回提交,使用
git reset --soft HEAD^
通过这种方式就撤销了你的commit
注意,仅仅是撤回commit操作,写的代码仍然保留。
git remote set-url origin newUrl
2.修改git文件下的config文件,修改其中的git remote orign
3.命令行修改
git remote rm origin
git remote add origin newUrl
Promise是一种异步解决方案,可以有效的解决基于回调函数形式而造成的回调地狱。在ES6中正式成为规范。在工作中会经常用到,同时在面试中也是必考的考点,所以我们不仅仅要掌握它的用法,也要明白它的原理。
Promise规范
Promise规范要求摘要
Promise源码实现
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const isFunc = params => typeof params === 'function'
class Promise {
constructor(executor) {
if (!isFunc(executor)) throw new Error('Promise only accept a function as a parameter')
// 状态
this._status = PENDING
// 数据
this._value = undefined
// 成功回调队列
this._fulfillQueues = []
// 失败回调队列
this._rejectQueues = []
try {
executor(this._resolve.bind(this), this._reject.bind(this))
} catch (error) {
this._reject(error)
}
}
// resolve时候执行的函数
_resolve(value) {
if (this._status === PENDING) {
this._status = FULFILLED
this._value = value
// 2.2.2.3 回调函数只能调用一次
let cb = this._fulfillQueues.shift()
while(cb) {
cb(value)
cb = this._fulfillQueues.shift()
}
// 队列被清空
}
}
// reject时候执行的函数
_reject(reason) {
if (this._status === PENDING) {
this._status = REJECTED
this._value = reason
// 2.2.3.3 回调函数只能调用一次
let cb = this._rejectQueues.shift()
while(cb) {
cb(reason)
cb = this._rejectQueues.shift()
}
// 队列被清空
}
}
// then
then(onFulfilled, onRejected) {
// 2.2.1 onFulfilled, onRejected 都是可选的
// 2.2.5 onFulfilled, onRejected 必须作为函数调用
// 2.2.7.3 若 onFulfilled ,不是一个函数,当前promise1为完成状态,
// 那promise2也是完成状态,且值promise1的 value
// 2.2.7.3 若 onRejected ,不是一个函数,当前promise1为失败状态,
// 那promise2也是失败状态,且值为promise1的 reason
onFulfilled = isFunc(onFulfilled) ? onFulfilled : value => value
onRejected = isFunc(onRejected) ? onRejected : reason => { throw reason }
const context = this
// 2.2.7 then必须返回一个promise
const promise2 = new Promise(function(resolve, reject){
if (context._status === FULFILLED) {
// 2.2.2 如果 onFulfilled 是函数,必须在promise1 fulfilled之后调用,其参数为promise1的value
// 2.2.4 onFulfilled,必须异步的调用
// 3.1 可以使用微任务 MutationObserver 或者 process.nextTick. 或者使用宏任务 setTimeout , setImmediate.
setTimeout(() => {
try {
// 2.2.7.1 如果 onFulfilled, onRejected 返回了值 x ,运行
// the Promise Resolution Procedure [[Resolve]](promise2, x)
let x = onFulfilled(context._value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
// 2.2.7.2 如果 onFulfilled, onRejected 抛出了错误,那 promise2 就 reject(error)
reject(error)
}
})
return
}
if (context._status === REJECTED) {
// 2.2.3 如果 onRejected 是函数,必须在promise1 rejected 之后调用,其参数为promise1的value
// 2.2.4 onRejected ,必须异步的调用
// 3.1 可以使用微任务 MutationObserver 或者 process.nextTick. 或者使用宏任务 setTimeout , setImmediate.
setTimeout(() => {
try {
// 2.2.7.1 如果 onFulfilled, onRejected 返回了值 x ,运行
// the Promise Resolution Procedure [[Resolve]](promise2, x)
let x = onRejected(context._value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
// 2.2.7.2 如果 onFulfilled, onRejected 抛出了错误,那 promise2 就 reject(error)
reject(error)
}
})
return
}
if (context._status === PENDING) {
context._fulfillQueues.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(context._value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
context._rejectQueues.push(() => {
setTimeout(() => {
try {
let x = onRejected(context._value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
})
}
})
return promise2
}
// 用于测试
// defer
static defer() {
return defer()
}
static deferred() {
return defer()
}
// promise 规范只要求我们必须实现 then 方法,但是es6规范还要求一些静态方法与实例方法,如
// 静态方法:Promise.resolve, Promise.reject, Promise.all, Promise.race
// 实例方法:catch, finally
// 在ES6标准入门中还有 done 方法,面试题中还有要实现 retry 方法
// catch
catch(onRejected) {
return this.then(undefined, onRejected)
}
// done
// 总是处于回调链的尾端,保证抛出任何可能的错误
done(onFulfilled, onRejected) {
this.then(onFulfilled, onRejected)
.catch(reason => {
// 全局抛出一个错误
setTimeout(() => { throw reason }, 0)
})
}
// finally
// 和 done 的最大区别是,接受一个普通的回调函数作为参数,该函数不管怎样都会执行
finally(handle) {
return this.then(
value => Promise.resolve(handle()).then(() => value),
reason => Promise.resolve(handle()).then(() => { throw reason })
)
}
// staic resolve
static resolve(value) {
if(value instanceof Promise) return value
return new Promise(resolve => resolve(value))
}
// static reject
static reject(reason) {
return new Promise((resolve, reject) => reject(reason))
}
// parameter #1, the function to return a Promise
// parameter #2, the max retry times
// parameter #3, the delay between each attempt
static retry(fn, times, delay) {
return new Promise((resolve, reject) => {
function attempt() {
fn().then(resolve)
.catch(err => {
if (times === 0) {
reject(err)
} else {
times--
setTimeout(() => {
attempt()
}, delay)
}
})
}
attempt()
})
}
// static all
all(promises) {
return new Promise((resolve, reject) => {
let count = 0
let results = []
let len = promises.length
if (!promises.length) {
// 空的可迭代对象,直接返回
resolve(results)
} else {
for(let i = 0; i < len; i++) {
Promise.resolve(promises[i]).then(
value => {
results[i] = value
count++
// 所有promise都完成后, resolve
if(count === len) resolve(results)
},
// 有一个发生错误,直接reject
error => reject(error)
)
}
}
})
}
// static race
race(promises) {
return new Promise((resolve, reject) => {
for(let p of promises) {
// 第一个 Promise 完成或者失败后,直接返回结果
Promise.resolve(p).then(
value => resolve(value),
reason => reject(reason)
)
}
})
}
}
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1 如果promise2和x是同一个对象,使用一个 TypeError 来 reject promise
if (promise2 === x) {
reject(new TypeError('Chaining cycle'))
}
if (x && typeof x === 'object' || isFunc(x)) {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被调用了,或者
// 相同的参数被多次调用了,那么只取第一次调用,后面的调用忽略掉
let used;
try {
// 2.3.3.1 把 x.then 赋值给 then
const then = x.then
// 2.3.3.3 如果then是一个函数,调用它,把x作为this,
// resolvePromise 作为第一个参数,rejectPromise 作为第二个参数
if (isFunc(then)) {
then.call(
x,
// 2.3.3.3.1 如果/当使用值y来调用resolvePromise时候,运行
// resolvePromise(promise2, y, resolve, reject)
y => {
if(used) return
used = true
resolvePromise(promise2, y, resolve, reject)
},
// 2.3.3.3.2 如果/当使用值r来调用 作为第一个参数,rejectPromise 时候,使用
// 值 r 来 reject promise
r => {
if(used) return
used = true
reject(r)
}
)
} else {
// 2.3.3.4 如果 then 不是函数,使用 x 来 fulfill promsie
if(used) return
used = true
resolve(x)
}
} catch (error) {
// 2.3.3.2 如果 2.3.3.1发生了错误,使用error来reject promise
if(used) return
used = true
reject(error)
}
} else {
// 2.3.4 如果 x 不是对象或函数, 使用 x 来 fulfill promsie
resolve(x)
}
}
function defer() {
const dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
需求背景
有一个表单,上面有上传图片和上传附件的功能,还有一些表单字段需要填写,最后提交表单。共有三个接口,上传图片接口,上传附件的接口,提交表单数据的接口。需要实现先上传图片和附件,获取到上传的地址后提交表单。
代码实现
import { from, zip } from 'rxjs';
import { concatMap } from 'rxjs/operators';
const makePromise = (name, delay) => new Promise((resolve, reject) => setTimeout(() => resolve(`${name} done`), delay));
const uploadImgs$ = from(makePromise('uploadImgs', 1000));
const uploadFiles$ = from(makePromise('uploadFiles', 2000));
const submitForm$ = from(makePromise('submitForm', 1000));
const subInst = zip(
uploadImgs$,
uploadFiles$
).pipe(
concatMap(val => {
console.log('val', val);
return submitForm$
})
).subscribe(
data => {
console.log('data', data)
},
error => {
console.log('error', error)
}
);
// async await实现
async function submitForm () {
const val = await Promise.all([
makePromise('uploadImgs', 1000),
makePromise('uploadFiles', 2000)
]);
console.log('val', val);
const val2 = await makePromise('submitForm', 1000);
console.log('val2', val2);
}
submitForm()
解读
实现的关键在于zip创建符。它的作用类似于我们衣服的拉链咬合的过程。会将我们输入的Observable组合起来,等待所有的的Observable每次完成之后输出结果,因为我们的只运行一次就结束了,所有只输出一次结果。它运行的时间取决于耗时最长的那个。待我们拿到结果后,可以结果传给另一个Observable,因为要继续处理Observable,所有我们需要用到高阶Observable。高阶Observable和高阶函数**类似。输入Observable,返回Observable,我们的操作符会自动订阅返回的Observable,并将最终结果输出。
上面的代码假设uploadImgs$ 耗时1s,uploadFiles$耗时2s。所以zip处理的时间为2s,2s后将结果返回给下游。下游收到数据后,经过submitForm$处理,1s后返回结果。整个过程耗时3s多。
另外,zip除了可以作为静态方法外,还可以作为操作符;我们也可以mergeMap来处理高阶Observable。在这个实例中没有大的区别。
实际中,常用的都是ajax请求,这个实例为了演示使用了setTimeout。
基于Webpack和React搭建的多页面脚手架,支持热重载。源码在src下面的webpack-react-multi-pages里面
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.