promise ajax 超时 中断

用到 promise.race

function waitWithTimeout(promise, timeout, timeoutMessage = "timeout") {
    let timer;
    const timeoutPromise = new Promise((_, reject) => {
        timer = setTimeout(() => reject(timeoutMessage), timeout);
    });

    return Promise.race([timeoutPromise, promise])
        .finally(() => clearTimeout(timer));    // 别忘了清 timer
}

(async () => {
    const business = new Promise(resolve => setTimeout(resolve, 1000 * 10));

    try {
        await waitWithTimeout(business, 1000);
        console.log("[Success]");
    } catch (err) {
        console.log("[Error]", err);    // [Error] timeout
    }
})();
// 上述,返回一个对象,对象里有 resolve, reject,promise
// 如果想干掉这个请求, obj.reject();

axios 超时

侥幸的是 Axios 的确能够解决超时,只须要在 options 里增加一个 timeout: 3000 就能解决问题。如果超时,能够在 catch 块中检测并解决:

try {...}
catch (err) {
    if (err.isAxiosError && !err.response && err.request
        && err.message.startsWith("timeout")) {
        // 如果是 Axios 的 request 谬误,并且音讯是延时音讯
        // TODO 解决超时
    }
}
finally {...}

解决 fetch() 超时

fetch() 本人不具备解决超时的能力,须要咱们判断超时后通过 AbortController 来触发“勾销”申请操作。如果须要中断一个 fetch() 操作,只需从一个 AbortController 对象获取 signal,并将这个信号对象作为 fetch() 的选项传入。大略就是这样:

const ac = new AbortController();
const { signal } = ac;
fetch(url, { signal }).then(res => {
    // TODO 解决业务
});

// 1 秒后勾销 fetch 操作
setTimeout(() => ac.abort(), 1000);

ac.abort() 会向 signal 发送信号,触发它的 abort 事件,并将其 .aborted 属性置为 truefetch() 外部解决会利用这些信息停止掉申请。

如果应用 await 的模式来解决,须要把 setTimeout(...) 放在 fetch(...) 之前:

完整封装

async function fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = new AbortController();
    const signal = ac.signal;
    
    const timer = setTimeout(() => {
        console.log("It's timeout");
        return ac.abort();
    }, timeout);
    
    try {
        return await fetch(resoure, { ...init, signal });
    } finally {
        clearTimeout(timer);
    }
}

ecma 6

Reflect :
将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。

命令式 变成 函数式
// 老写法
‘assign’ in Object // true

// 新写法
Reflect.has(Object, ‘assign’) // true

////
Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为
[cc lang=”javascript”]
Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target, name, value, receiver);
if (success) {
console.log(‘property ‘ + name + ‘ on ‘ + target + ‘ set to ‘ + value);
}
return success;
}
});[/cc]

///////////
Reflect对象一共有 13 个静态方法。

Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)

上面这些方法的作用,大部分与Object对象的同名方法的作用都是相同的,而且它与Proxy对象的方法是一一对应的。下面是对它们的解释。

promise then 和catch 方法

then(function) 参数不是fun 则跳过,执行下个then,
function 的返回值 会作为下一个then里回调函数的参数 如果没有返回值则是undefined
如果是function 返回 异步操作,要等待 异步操作返回值

material-UI

clone 属性

!!响应式字体大小
[cc lang=”js”]const theme = createMuiTheme();

theme.typography.h1 = {
fontSize: ‘3rem’,
‘@media (min-width:600px)’: {
fontSize: ‘4.5rem’,
},
[theme.breakpoints.up(‘md’)]: {
fontSize: ‘6rem’,
},
};[/cc]

自定义变量
[cc lang=”js”]

import Checkbox from ‘@material-ui/core/Checkbox’;
import { createMuiTheme, withStyles } from ‘@material-ui/core/styles’;
import { ThemeProvider } from ‘@material-ui/styles’;
import { orange } from ‘@material-ui/core/colors’;

const styles = theme => ({
root: {
color: theme.status.danger,
‘&$checked’: {
color: theme.status.danger,
},
},
checked: {},
});

let CustomCheckbox = props => (

);

CustomCheckbox.propTypes = {
classes: PropTypes.object.isRequired,
};

CustomCheckbox = withStyles(styles)(CustomCheckbox);

const theme = createMuiTheme({
status: {
// My business variables
danger: orange[500],
},
});

function CustomStyles() {
return (



);
}

export default CustomStyles;[/cc]

****************
每个 material-ui 组件都有一个 class 类, theme 可以从写这个类,类的样式可以到组件详情里看到
当配置变量不够强大的时候,您可以使用theme的overrides来让Material-UI隐式地为您注入样式规则。 这是一个非常强大的特性。

[cc lang=”js”]const theme = createMuiTheme({
overrides: {
// Style sheet name ⚛️
MuiButton: {
// Name of the rule
text: {
// Some CSS
color: ‘white’,
},
},
},
});[/cc]
**************

ajax axios fetch 请求方法对比

axios:
axios.post(url[, data[, config]])
axios.get(url[, config])

axios({
method:’get’,
url:’http://bit.ly/2mTM3nY’,
responseType:’stream’
})
.then(function (response) {
response.data.pipe(fs.createWriteStream(‘ada_lovelace.jpg’))
});

fetch:
fetch(url, {config})
.then(
if (response.ok) {
return response.json()
} else {
// Find some way to get to execute .catch()
return Promise.reject(‘something went wrong!’)
}
)
.then(data => console.log(‘data is’, data))
.catch(error => ({ error }));

0. 断网 // 只会走catch 函数
以下都会走then
1. 可能尝试获取不存在的资源 404
2. 没有权限获取资源
3. 输入参数有误
4. 服务器抛出异常
5. 服务器超时
6. 服务器崩溃
7. API更改 …

[cc lang=”javascript”]
// 返回消息 response 对象
{
body: ReadableStream
bodyUsed: false
headers: Headers
ok : true
redirected : false
status : 200
statusText : “OK”
type : “cors”
url : “http://some-website.com/some-url”
__proto__ : Response
}
// response.json() | response.text() | response.blob() 等这些方法都返回promise
[/cc]

preload 和 prefetch

preload 是告诉浏览器页面必定需要的资源,浏览器一定会加载这些资源
prefetch 是告诉浏览器页面可能需要的资源,浏览器不一定会加载这些资源
prefetch ,它的作用是告诉浏览器加载下一页面可能会用到的资源,注意,是下一页面,而不是当前页面。因此该方法的加载优先级非常低,也就是说该方式的作用是加速下一个页面的加载速度

preload 好处:
1、将加载和执行分离开,不阻塞渲染和document的onload事件
2、提前加载指定资源,不再出现依赖的font字体隔了一段时间才刷出的情况
3、Preload 有 as 属性,浏览器可以设置正确的资源加载优先级,这种方式可以确保资源根据其重要性依次加载, 所以,Preload既不会影响重要资源的加载,又不会让次要资源影响自身的加载;浏览器能根据 as 的值发送适当的 Accept 头部信息;浏览器通过 as 值能得知资源类型,因此当获取的资源相同时,浏览器能够判断前面获取的资源是否能重用
4、如果忽略 as 属性,或者错误的 as 属性会使 preload 等同于 XHR 请求,浏览器不知道加载的是什么,因此会赋予此类资源非常低的加载优先级

preload 二次获取
1、如果对所 preload 的资源不使用明确的 “as” 属性,将会导致二次获取
2、 preload 字体不带 crossorigin 会二次获取! 确保对 preload 的字体添加 crossorigin 属性,否则字体文件会被下载两次,这个请求使用匿名的跨域模式。这个建议也适用于字体文件在相同域名下,也适用于其他域名的获取(比如说默认的异步获取)

webpack
webpack 4.6.0+ adds support for prefetching and preloading.

1 preload 加载平行于 主要加载项目, prefetch 在主要加载项完成后加载
2 preload 中等优先度,会立即下载, prefetch 会在空闲(idle)时下载
3 preload 内容 通过 父chunk 会被立即用到, prefetch 内容会在将来用到

prefetch 案例: HomePage component 里有 LoginBotton component , 点击 按钮是会加载LoginModal
LoginButton.js
[cc lang=”js”]import(/* webpackPrefetch: true */ ‘LoginModal’);[/cc]

result in

prelaod 案例: ChartComponent 需要 比较大的 ChartingLibrary, 加载完页面就需要用到ChartingLibrary 里的 LoadingIndicator
ChartComponent.js
[cc lang=”js”]import(/* webpackPreload: true */ ‘ChartingLibrary’);[/cc]

https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf
https://medium.com/webpack/link-rel-prefetch-preload-in-webpack-51a52358f84c

使用 new Function() 模拟实现的apply, call

就是利用对象上的方法去执行,this 指向对象;
1.模拟实现apply


// 浏览器环境 非严格模式 ;指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装;
function getGlobalObject(){
  return this;
}

function generateFunctionCode(argsArrayLength){
  var code = 'return arguments[0][arguments[1]](';
  for(var i = 0; i < argsArrayLength; i++){ 
     if(i > 0){
       code += ',';
     }
   code += 'arguments[2][' + i + ']';
  }
  code += ')';
  // return arguments[0][arguments[1]](arg1, arg2, arg3...)
  return code;
}

Function.prototype.applyFn = function apply(thisArg, argsArray){ 
  // `apply` 方法的 `length` 属性是 `2`。
  // 1.如果 `IsCallable(func)` 是 `false`, 则抛出一个 `TypeError` 异常。

  if(typeof this !== 'function'){
    throw new TypeError(this + ' is not a function');
  }
  // 2.如果 argArray 是 null 或 undefined, 则
  // 返回提供 thisArg 作为 this 值并以空参数列表调用 func 的 [[Call]] 内部方法的结果。
  if(typeof argsArray === 'undefined' || argsArray === null){
    argsArray = [];
  }
  // 3.如果 Type(argArray) 不是 Object, 则抛出一个 TypeError 异常 .
  if(argsArray !== new Object(argsArray)){
    throw new TypeError('CreateListFromArrayLike called on non-object');
  }
  if(typeof thisArg === 'undefined' || thisArg === null){
    // 在外面传入的 thisArg 值会修改并成为 this 值。
    // ES3: thisArg 是 undefined 或 null 时它会被替换成全局对象 浏览器里window
    thisArg = getGlobalObject();
  }
  // ES3: 所有其他值会被应用 ToObject 并将结果作为 this 值,这是第三版引入的更改。
  thisArg = new Object(thisArg);
  var __fn = '__' + new Date().getTime();
  // 万一还是有 先存储一份,删除后,再恢复该值
  var originalVal = thisArg[__fn];
  // 是否有原始值
  var hasOriginalVal = thisArg.hasOwnProperty(__fn);
  thisArg[__fn] = this;
  // 9.提供 `thisArg` 作为 `this` 值并以 `argList` 作为参数列表,调用 `func` 的 `[[Call]]` 内部方法,返回结果。

  // ES6版
  // var result = thisArg[__fn](...args);
  var code = generateFunctionCode(argsArray.length);
  var result = (new Function(code))(thisArg, __fn, argsArray);
  delete thisArg[__fn];
  if(hasOriginalVal){
    thisArg[__fn] = originalVal;
  }
  return result;
};

2.模拟实现call

Function.prototype.callFn = function call(thisArg){
var argsArray = [];
var argumentsLength = arguments.length;

for(var i = 0; i < argumentsLength - 1; i++){ 
// argsArray.push(arguments[i + 1]); 
  argsArray[i] = arguments[i + 1]; 
} 
console.log('argsArray:', argsArray); 
return this.applyFn(thisArg, argsArray); 
} 
// 测试例子 
var doSth = function (name, age){ 
var type = Object.prototype.toString.call(this); 
console.log(typeof doSth); 
console.log(this === firstArg); 
console.log('type:', type); 
console.log('this:', this); 
console.log('args:', [name, age], arguments); 
  return 'this--'; 
}; 
var name = 'window'; 
var student = { name: 'xiaoming', age: 18, doSth: 'doSth', __fn: 'doSth', }; 
var firstArg = student; 
var result = doSth.applyFn(firstArg, [1, {name: 'daming'}]); 
var result2 = doSth.callFn(firstArg, 1, {name: 'daming'}); console.log('result:', result); 
console.log('result2:', result2);

async/await/promise/ setTimeout/ event loop

promise:
[cc lang=”javascript”]
const p1 = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error(‘fail’)), 3000)
})

const p2 = new Promise(function (resolve, reject) {
setTimeout(() => resolve(p1), 1000)
})

p2
.then(result => console.log(result))
.catch(error => console.log(error))
// Error: fail
[/cc]
上面代码中,p1是一个 Promise,3 秒之后变为rejected。p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

!!! 注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。如下
[cc lang=”js”]
new Promise((resolve, reject) => {
resolve(1); //这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
如果加上return语句就不会打印2
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
[/cc]
promise then 方法
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
then里的 回调函数完成以后,会将返回结果作为参数,传入下一次回调函数。如果回调函数返回promise,这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用
promise catch 方法
Promise.prototype.catch方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数.
建议用catch 不要用then 第二个方法
!!! catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法

promise.all([ p1, p2, p3 ]).then().catch

promise.race([ p1, p2, p3]).then().catch
那个率先改变的 Promise 实例的返回值,就传递给then或catch的回调函数
[cc lang=”js”]
const p = Promise.race([
fetch(‘/resource-that-may-take-a-while’),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error(‘request timeout’)), 5000)
})
]);

p
.then(console.log)
.catch(console.error);
//上面代码中,如果 5 秒之内fetch方法无法返回结果,变量p的状态就会变为rejected,从而触发catch方法指定的回调函数

[/cc]

1.async 函数返回的是一个 Promise 对象,async 函数没有返回值,它会返回 Promise.resolve(undefined)

2 await 如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
如果它等到的是一个 Promise 对象,await就忙起来了,它会阻塞asycn函数内后面的代码,等着Promise对象resolve,然后得到 resolve 的值,作为 await 表达式的运算结果
3。1 js 执行机制 event loop
首先判断JS是同步还是异步,同步就进入主进程,异步就进入event table
异步任务在event table中注册函数,当满足触发条件后,被推入event queue
同步任务进入主线程后一直执行,直到主线程空闲时,才会去event queue中查看是否有可执行的异步任务,如果有就推入主进程中
3.2event loop (2)
macro-task(宏任务):包括整体代码script,setTimeout,setInterval
micro-task(微任务):Promise,process.nextTick

!! 执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的【事件队列】里
当前宏任务执行完成后,会查看微任务的【事件队列】,并将里面全部的微任务依次执行完
[cc lang=”javascript”]
async function async1() {
console.log( ‘async1 start’ )
await async2() // return promise 此时返回的Promise会被放入到回调队列中等待,await会让出线程
//等外面执行完成后再回到这生成promise.resolve(undefined) 又被放到micro-task, 再次让出线程跳出async
console.log( ‘async1 end’ )
}

async function async2() {
console.log( ‘async2’)
}

console.log( ‘script start’ )

setTimeout( function () {
console.log( ‘setTimeout’ )
}, 0 )

async1();

new Promise( function ( resolve ) {
console.log( ‘promise1’ )
resolve(); // 回调then 会放入micro-task 让出线程
console.log(34)
} ).then( function () {
console.log( ‘promise2’ )
return Promise.resolve();
} ).then(function(){console.log(11)
return Promise.resolve();
}).then(function(){console.log(22)})

console.log( ‘script end’ )

//script start
//async1 start
//async2
//promise1
// 34
//script end
//promise2
//async1 end
//11
//22
// setTimeout
[/cc]

你不知道的javascript

1.  实际上,undefined 值是派生自null值的,因此ECMA-262规定对它们的相等性测试要返回true:

alert(null == undefined); //true

2. 浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1加0.2的结果不是0.3,而是0.30000000000000004。这个小小的舍入误差会导致无法测试特定的浮点数值。例如:

if (a + b == 0.3){ // 不要做这样的测试! alert("You got 0.3."); }

3. 数值转换 有3个函数可以把非数值转换为数值:Number()、parseInt()和parseFloat()。第一个函数,即转型函数Number()可以用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。

Number()函数的转换规则如下。与一元 操作符+有相同效果

  • 如果是Boolean值,true和false将分别被转换为1和0。
  • 如果是数字值,只是简单的传入和返回。  如果是null值,返回0。
  • 如果是undefined,返回NaN。
  • 如果是字符串,遵循下列规则:
  • 如果字符串中只包含数字(包括前面带正号或负号的情况),则将其转换为十进制数值,即”1″会变成1,”123″会变成123,而”011″会变成11(注意:前导的零被忽略了);
  • 如果字符串中包含有效的浮点格式,如”1.1″,则将其转换为对应的浮点数值(同样,也会忽略前导零);
  • 如果字符串中包含有效的十六进制格式,例如”0xf”,则将其转换为相同大小的十进制整 数值;
  • 如果字符串是空的(不包含任何字符),则将其转换为0;
  • 如果字符串中包含除上述格式之外的字符,则将其转换为NaN。
  • 如果是对象,则调用对象的 valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是 NaN,则调用对象的 toString()方法,然后再次依照前面的规则转换返回的字符 串值。

parseInt parseFloat

var num1 = parseInt("AF", 16); //175
var num2 = parseInt("AF"); //NaN parseFloat 没有第二个参数只能解析十进制;其他进制会被转成0
var num6 = parseFloat("3.125e7");//31250000

5. toString()可以输出以二进制、八进制、十六进制

6. 位操作符

ecmajavascript 所有数都以 IEEE-754 64位格式存储,但位操作是将64位转换成32的整数执行操作,然后再换回64位

无符号的整数 是32位
有符号的整数 是31位加第32位符号位

正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。

负数也是用二进制码存储, 但使用二进制补码 下面求二进制补码:

  • 1 一个数绝对值的二进制码
  • 2 求二进制反码, 即0 替换1, 1 替换0, 符号位不变
  • 3 得到的二进制反码加1 就是补码
  1. 按位非(NOT) 就是求反码 用 ~ 表示
  2. alert(~ 25) // -26 也就是25的负数二进制码 再减1 就是25的反码, 所以-25 -1
  3. 按位与(AND)用 & 表示
  4. 两个数值的对应位都是1 时才返回1,任何一位为0 都返回0
  5. alert(25 & 3) // 1
  6. 3 按位或(OR) 用 | 表示
  7. 两个数值的对应位有一个是1 就返回1
  8. alert (25 | 3) // 27
  9. 按位异或(XOR) 用 ^ 表示
  10. 两个数值对应位上 只有一个1时才返回1, 都是1或0 返回0
  11. alert (25 ^ 3) // 26
  12. 左移 用 << 表示, 左移不会影响符合位 2 << 5 // 64 2左移5位空出来的 用 0 补空位, 换算成十进制是 64 6 右移 用 >> 表示 右移出现的空位用符号位的值来补充

7. 传递参数

所有参数是按值传递的

function setName(obj){
   obj.name= 'nich';
   obj = new Object();
   obj.name = 'greg'
}
var person = {};
setName(person);
alert(person.name) // nich
//即使按值传递, obj 也会按引用访问同一个对象;
// 如果person 按引用传递的, 那name 属性值为greg,

8. 迭代方法

arr.every((val [,index] [,array]) => val ===1) //  每一项都是true 才返回true
arr.some() // 其中一项为true 就返回true

Function

函数内部属性 arguments 和 this

1.arguments 主要用途保存函数参数, 这个对象还有一个交 callee 的属性,该属性是个指针,指向arguments 的函数

 // 严格模式下反问 callee 会报错 
// 利用callee 阶乘
function factorial(num) {
  if(num <= 1){
    return 1;
  } else {
    return num * arguments.callee(num -1)
  }
}

2. caller 保存着调用当前函数的函数的引用, 严格模式会报错
argument.callee.caller 指向 调用arments 函数的函数

3. call 第二个参数可以是数组 可以是arguments 对象

Number

toPrecision(2) 方法 根据参数位数,返回最合适的格式表达数字

String

1 split()

var colorText = 'red, bule, green, yellow';
var colors = colorText.split(/[^\,]+/); // ['', ',', ',', ',', '']

2 replace()

function htmlEscape(text){
  return text.replace(
       /[ <>"&]/g, 
      function(macth, pos, originText){ 
        // 每一次的匹配项, 位置, 原始字符串
        switch(match){
          case "<"
        return "&lt"
        }
      }
 )
}

Object 操作符

原型与in 操作符
无论是实例属性还是原型属性 都会返回true
‘name’ in obj

  • obj.hanOwnProperty(‘name1’) name1只有存在于实例中的属性才返回true
  • 对象添加实例属性会屏蔽原型中属性, 但delete 后, 就会返回原型中的属性
  • for in 遍历enumerated 的属性, 既包括实例中也包括原型中的属性
  • Object.keys() 返回一个可枚举属性(包括方法)的字符串数组, 如果是对象只包含实例属性

function

函数创建时,会预先创建一个包含全局变量的作用域链, 这个作用域链被保存在函数内部 [[scope]],
当调用函数时, 会为函数创建一个执行环境,通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链,
此后又有一个活动对象(也就是函数的参数对象)被创建并推出执行环境作用域链的前端。

  • 作用域链的本质是一个指向变量对象的指针列表,只是引用
  • 闭包:有权访问另外一个函数作用域中的变量的函数
  • 闭包的作用域链包含外部函数的作用域链

闭包中的this 返回 window 问题:

  • 函数调用时会自动获取两个特殊变量 this和arguments
  • 闭包搜索这两个变量时, 只会搜索到其活动对象为止, 因此永远不能访问外部函数的这两个对象
var object = {
  name: 'my object',
  getName: function(){
    return function(){
      this.name // undefined
    }
  }
}
object.getName()() // undefined