当前位置: 首页 > article >正文

前端开发中ES6的技术细节二

ES6中Map Set使用上的区别:

//首先初始化数据​
var lng=100var arr =new Array(lng).fill(2)var set =new Set(arr)let map =new Map()for(var i=0;i<lng;i++){​
arr[i]=i​
map.set(i,arr[i])}// Array​
console.time()for(var j=0;j<lng;j++){​
arr.includes(j)}​
console.timeEnd()  //default: 0.01220703125 ms​
// Set​
console.time()for(var j=0;j<lng;j++){​
set.has(j)}​
console.timeEnd()  // default: 0.005859375 ms​
// Map​
console.time()for(var j=0;j<lng;j++){​
map.has(j)}​
console.timeEnd()// default: 0.007080078125 ms

通过以上几种方法我们可以看到,Set执行时间最短,那么查找速度最快,当然了Set 和 Map的查找速度都很快想差不大,所以说这两种方法具有极快的查找速度。​
(2) 初始化需要的值不一样,Map需要的是一个二维数组,而Set 需要的是一维 Array 数组​
(3) Map 和 Set 都不允许键重复​
(4) Map的键是不能修改,但是键对应的值是可以修改的;Set不能通过迭代器来改变Set的值,因为Set的值就是键。​
(5) Map 是键值对的存在,值也不作为健;而 Set 没有 value 只有 key,value 就是 key;

如何理解ES6中的Proxy 使用场景有哪些

一、介绍​
定义: 用于定义基本操作的自定义行为​
本质: 修改的是程序默认形为,就形同于在编程语言层面上做修改,属于元编程(meta programming)​
元编程(Metaprogramming,又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作​
一段代码来理解

#!/bin/bash​
# metaprogram​
echo '#!/bin/bash' >program​
for ((I=1; I<=1024; I++)) do​
    echo "echo $I" >>program​
done​
chmod +x program

这段程序每执行一次能帮我们生成一个名为program的文件,文件内容为1024行echo,如果我们手动来写1024行代码,效率显然低效​

  • 元编程优点:与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译

Proxy 亦是如此,用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

二、用法​
Proxy为 构造函数,用来生成 Proxy 实例

var proxy = new Proxy(target, handler)

参数​

  • target表示所要拦截的目标对象(任何类型的对象,包括原生数组,函数,甚至另一个代理))​
  • handler通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为

handler解析​
关于handler拦截属性,有如下:​

  • get(target,propKey,receiver):拦截对象属性的读取​
  • set(target,propKey,value,receiver):拦截对象属性的设置​
  • has(target,propKey):拦截propKey in proxy的操作,返回一个布尔值​
  • deleteProperty(target,propKey):拦截delete proxy[propKey]的操作,返回一个布尔值​
  • ownKeys(target):拦截Object.keys(proxy)、for…in等循环,返回一个数组​
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象​
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc),返回一个布尔值​
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值​
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象​
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值​
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值​
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作​
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作

Reflect​
若需要在Proxy内部调用对象的默认行为,建议使用Reflect,其是ES6中操作对象而提供的新 API​
基本特点:​

  • 只要Proxy对象具有的代理方法,Reflect对象全部具有,以静态方法的形式存在​
  • 修改某些Object方法的返回结果,让其变得更合理(定义不存在属性行为的时候不报错而是返回false)​
  • 让Object操作都变成函数行为​
    下面我们介绍proxy几种用法:

get()

get接受三个参数,依次为目标对象、属性名和 proxy 实例本身,最后一个参数可选

var person = {
  name: "张三"
};
var proxy = new Proxy(person, {
  get: function(target, propKey) {
    if (propKey in target) {
      return target[propKey];
    } else {
      throw new ReferenceError("Prop name \"" + propKey + "\" does not exist."  );
    }
  }
});
console.log(proxy.name);

get能够对数组增删改查进行拦截,下面是试下你数组读取负数的索引

function createArray(...elements) {let handler = {get(target, propKey, receiver) {let index = Number(propKey);if (index < 0) {​
        propKey = String(target.length + index);}return Reflect.get(target, propKey, receiver);}};​
​
  let target = [];​
  target.push(...elements);return new Proxy(target, handler);}​
​
let arr = createArray('a', 'b', 'c');​
arr[-1] // c

**注意:**如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则会报错

const target = Object.defineProperties({}, {​
  foo: {​
    value: 123,​
    writable: false,​
    configurable: false},});​
​
const handler = {get(target, propKey) {return 'abc';}};const proxy = new Proxy(target, handler);​
proxy.foo​
// TypeError: Invariant check failed

set()
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身​
假定Person对象有一个age属性,该属性应该是一个不大于 200 的整数,那么可以使用Proxy保证age的属性值符合要求.

let validator = {set: function(obj, prop, value) {if (prop === 'age') {if (!Number.isInteger(value)) {throw new TypeError('The age is not an integer');}if (value > 200) {throw new RangeError('The age seems invalid');}}​
​
    // 对于满足条件的 age 属性以及其他属性,直接保存​
    obj[prop] = value;}};​
​
let person = new Proxy({}, validator);​
​
person.age = 100;​
​
person.age // 100​
person.age = 'young' // 报错​
person.age = 300 // 报错

如果目标对象自身的某个属性,不可写且不可配置,那么set方法将不起作用

const obj = {};​
Object.defineProperty(obj, 'foo', {​
  value: 'bar',​
  writable: false,});​
​
const handler = {set: function(obj, prop, value, receiver) {​
    obj[prop] = 'baz';}};​
​
const proxy = new Proxy(obj, handler);​
proxy.foo = 'baz';​
proxy.foo // "bar"

注意,严格模式下,set代理如果没有返回true,就会报错

'use strict';const handler = {set: function(obj, prop, value, receiver) {​
    obj[prop] = receiver;// 无论有没有下面这一行,都会报错​
    return false;}};const proxy = new Proxy({}, handler);​
proxy.foo = 'bar';// TypeError: 'set' on proxy: trap returned falsish for property 'foo'

deleteProperty()
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除.

var handler = {deleteProperty (target, key) {invariant(key, 'delete');​
    Reflect.deleteProperty(target,key)return true;}};function invariant (key, action) {if (key[0] === '_') {throw new Error(`无法删除私有属性`);}}​​
var target = { _prop: 'foo' };var proxy = new Proxy(target, handler);delete proxy._prop​
// Error: 无法删除私有属性

注意,目标对象自身的不可配置(configurable)的属性,不能被deleteProperty方法删除,否则报错;

取消代理

Proxy.revocable(target, handler);

三、使用场景

Proxy其功能非常类似于设计模式中的代理模式,常用功能如下:​

  • 拦截和监视外部对对象的访问​
  • 降低函数或类的复杂度​
  • 在复杂操作前对操作进行校验或对所需资源进行管理​
  • 使用 Proxy 保障数据类型的准确性
let numericDataStore = { count: 0, amount: 1234, total: 14 };​
numericDataStore = new Proxy(numericDataStore, {set(target, key, value, proxy) {if (typeof value !== 'number') {throw Error("属性只能是number类型");}return Reflect.set(target, key, value, proxy);}});​
numericDataStore.count = "foo"// Error: 属性只能是number类型​
numericDataStore.count = 333// 赋值成功

声明了一个私有的 apiKey,便于 api 这个对象内部的方法调用,但不希望从外部也能够访问 api._apiKey​

let api = {​
    _apiKey: '123abc456def',getUsers: function(){ },getUser: function(userId){ },setUser: function(userId, config){ }};const RESTRICTED = ['_apiKey'];​
api = new Proxy(api, {get(target, key, proxy) {if(RESTRICTED.indexOf(key) > -1) {throw Error(`${key} 不可访问.`);} return Reflect.get(target, key, proxy);},set(target, key, value, proxy) {if(RESTRICTED.indexOf(key) > -1) {throw Error(`${key} 不可修改`);} return Reflect.get(target, key, value, proxy);}});​
console.log(api._apiKey)​
api._apiKey = '987654321'// 上述都抛出错误

还能通过使用Proxy实现观察者模式​
观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行​
observable函数返回一个原始对象的 Proxy 代理,拦截赋值操作,触发充当观察者的各个函数

const queuedObservers = new Set();​
​
const observe = fn => queuedObservers.add(fn);const observable = obj => new Proxy(obj, {set});​
​
function set(target, key, value, receiver) {const result = Reflect.set(target, key, value, receiver);​
  queuedObservers.forEach(observer => observer());return result;}

如何理解ES6中的Generator 使用场景有哪些?

一、介绍​
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同​
回顾下上文提到的解决异步的手段:

  • 回调函数
  • Promise

那么,上文我们提到promsie已经是一种比较流行的解决异步方案,那么为什么还出现Generator?甚至async/await呢?​
该问题我们留在后面再进行分析,下面先认识下Generator

Generator函数
执行 Generator 函数会返回一个遍历器对象,可以依次遍历 Generator 函数内部的每一个状态​
形式上,Generator 函数是一个普通函数,但是有两个特征:​

  • function关键字与函数名之间有一个星号​
  • 函数体内部使用yield表达式,定义不同的内部状态
function* helloWorldGenerator() {yield 'hello';yield 'world';return 'ending';}

二、使用
Generator 函数会返回一个遍历器对象,即具有Symbol.iterator属性,并且返回给自己

function* gen(){// some code​
}var g = gen();​
g[Symbol.iterator]() === g​
// true

通过yield关键字可以暂停generator函数返回的遍历器对象的状态

function* helloWorldGenerator() {yield 'hello';yield 'world';return 'ending';}var hw = helloWorldGenerator();

通过next方法才会遍历到下一个内部状态,其运行逻辑如下:​

  • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。​
  • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式​
  • 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。​
  • 如果该函数没有return语句,则返回的对象的value属性值为undefined
hw.next()// { value: 'hello', done: false }​
​
hw.next()// { value: 'world', done: false }​
​
hw.next()// { value: 'ending', done: true }​
​
hw.next()// { value: undefined, done: true }

done用来判断是否存在下个状态,value对应状态值​
yield表达式本身没有返回值,或者说总是返回undefined​
通过调用next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值.

function* foo(x) {var y = 2 * (yield (x + 1));var z = yield (y / 3);return (x + y + z);}​
​
var a = foo(5);​
a.next() // Object{value:6, done:false}​
a.next() // Object{value:NaN, done:false}​
a.next() // Object{value:NaN, done:true}​var b = foo(5);​
b.next() // { value:6, done:false }​
b.next(12) // { value:8, done:false }​
b.next(13) // { value:42, done:true }

正因为Generator 函数返回Iterator对象,因此我们还可以通过for…of进行遍历

function* foo() {yield 1;yield 2;yield 3;yield 4;yield 5;return 6;}​
​
for (let v of foo()) {​
  console.log(v);}// 1 2 3 4 5

原生对象没有遍历接口,通过Generator 函数为它加上这个接口,就能使用for…of进行遍历了

三、异步解决方案
回顾之前展开异步解决的方案:​

  • 回调函数​
  • Promise 对象​
  • generator 函数​
  • async/await​
    这里通过文件读取案例,将几种解决异步的方案进行一个比较:

回调函数

所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,再调用这个函数

fs.readFile('/etc/fstab', function (err, data) {if (err) throw err;​
  console.log(data);​
  fs.readFile('/etc/shells', function (err, data) {if (err) throw err;​
    console.log(data);});});

readFile函数的第三个参数,就是回调函数,等到操作系统返回了/etc/passwd这个文件以后,回调函数才会执行

Promise
Promise就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用

const fs = require('fs');​
​
const readFile = function (fileName) {return new Promise(function (resolve, reject) {​
    fs.readFile(fileName, function(error, data) {if (error) return reject(error);resolve(data);});});};​
​
​
readFile('/etc/fstab').then(data =>{​
    console.log(data)return readFile('/etc/shells')}).then(data => {​
    console.log(data)})

这种链式操作形式,使异步任务的两段执行更清楚了,但是也存在了很明显的问题,代码变得冗杂了,语义化并不强

generator

yield表达式可以暂停函数执行,next方法用于恢复函数执行,这使得Generator函数非常适合将异步任务同步化

const gen = function* () {const f1 = yield readFile('/etc/fstab');const f2 = yield readFile('/etc/shells');​
  console.log(f1.toString());​
  console.log(f2.toString());};

async/await
将上面Generator函数改成async/await形式,更为简洁,语义化更强了

const asyncReadFile = async function () {const f1 = await readFile('/etc/fstab');const f2 = await readFile('/etc/shells');​
  console.log(f1.toString());​
  console.log(f2.toString());};

区别:
通过上述代码进行分析,将promise、Generator、async/await进行比较:​

  • promise和async/await是专门用于处理异步操作的​
  • Generator并不是为异步而设计出来的,它还有其他功能(对象迭代、控制输出、部署Interator接口…)​
  • promise编写代码相比Generator、async更为复杂化,且可读性也稍差​
  • Generator、async需要与promise对象搭配处理异步情况​
  • async实质是Generator的语法糖,相当于会自动执行Generator函数​
  • async使用上更为简洁,将异步代码以同步的形式进行编写,是处理异步编程的最终方案

四、使用场景

Generator是异步解决的一种方案,最大特点则是将异步操作同步化表达出来

function* loadUI() {showLoadingScreen();yield loadUIDataAsynchronously();hideLoadingScreen();}var loader = loadUI();// 加载UI​
loader.next()​
​
// 卸载UI​
loader.next()

包括redux-saga 中间件也充分利用了Generator特性

import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'import Api from '...'​
​
function* fetchUser(action) {try {const user = yield call(Api.fetchUser, action.payload.userId);yield put({type: "USER_FETCH_SUCCEEDED", user: user});} catch (e) {yield put({type: "USER_FETCH_FAILED", message: e.message});}}​
​
function* mySaga() {yield takeEvery("USER_FETCH_REQUESTED", fetchUser);}​
​
function* mySaga() {yield takeLatest("USER_FETCH_REQUESTED", fetchUser);}​
​
export default mySaga;

还能利用Generator函数,在对象上实现Iterator接口

function* iterEntries(obj) {let keys = Object.keys(obj);for (let i=0; i < keys.length; i++) {let key = keys[i];yield [key, obj[key]];}}​
​
let myObj = { foo: 3, bar: 7 };​
​
for (let [key, value] of iterEntries(myObj)) {​
  console.log(key, value);}​
​
// foo 3​
// bar 7

如何理解ES6中的Promise

一、介绍​
Promise ,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大​
在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码

doSomething(function(result) {doSomethingElse(result, function(newResult) {doThirdThing(newResult, function(finalResult) {​
      console.log('得到最终结果: ' + finalResult);}, failureCallback);}, failureCallback);}, failureCallback);

阅读上面代码,是不是很难受,上述形成了经典的回调地狱​
现在通过Promise的改写上面的代码

doSomething().then(function(result) {return doSomethingElse(result);}).then(function(newResult) {return doThirdThing(newResult);}).then(function(finalResult) {​
  console.log('得到最终结果: ' + finalResult);}).catch(failureCallback);

瞬间感受到promise解决异步操作的优点:​
链式操作减低了编码难度​
代码可读性明显增强​
下面我们正式来认识promise:
状态​
promise对象仅有三种状态​

  • pending(进行中)​
  • fulfilled(已成功)​
  • rejected(已失败)

特点​

  • 对象的状态不受外界影响,只有异步操作的结果,可以决定当前是哪一种状态​
  • 一旦状态改变(从pending变为fulfilled和从pending变为rejected),就不会再变,任何时候都可以得到这个结果

流程
在这里插入图片描述
二、用法
Promise对象是一个构造函数,用来生成Promise实例

const promise = new Promise(function(resolve, reject) {});

Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject​

  • resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”​
  • reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”

实例方法​

Promise构建出来的实例存在以下方法:​

  • then()​
  • catch()​
  • finally()

then()

then是实例状态发生改变时的回调函数,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数​
then方法返回的是一个新的Promise实例,也就是promise能链式书写的原因

getJSON("/posts.json").then(function(json) {return json.post;}).then(function(post) {// ...​
});

catch
catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数

getJSON('/posts.json').then(function(posts) {// ...​
}).catch(function(error) {// 处理 getJSON 和 前一个回调函数运行时发生的错误​
  console.log('发生错误!', error);});

Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止

getJSON('/post/1.json').then(function(post) {return getJSON(post.commentURL);}).then(function(comments) {// some code​
}).catch(function(error) {// 处理前面三个Promise产生的错误​
});

一般来说,使用catch方法代替then()第二个参数​
Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应

const someAsyncThing = function() {return new Promise(function(resolve, reject) {// 下面一行会报错,因为x没有声明​
    resolve(x + 2);});};

浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程​
catch()方法之中,还能再抛出错误,通过后面catch方法捕获到

finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

promise​
.then(result => {···}).catch(error => {···}).finally(() => {···});

构造函数方法
Promise构造函数存在以下方法:​

  • all()​
  • race()​
  • allSettled()​
  • resolve()​
  • reject()​
  • try()

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

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

接受一个数组(迭代对象)作为参数,数组成员都应为Promise实例​
实例p的状态由p1、p2、p3决定,分为两种:​

  • 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数​
  • 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数​

注意,如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法

const p1 = new Promise((resolve, reject) => {resolve('hello');}).then(result => result).catch(e => e);​
​
const p2 = new Promise((resolve, reject) => {throw new Error('报错了');}).then(result => result).catch(e => e);​
​
Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));// ["hello", Error: 报错了]

如果p2没有自己的catch方法,就会调用Promise.all()的catch方法

const p1 = new Promise((resolve, reject) => {resolve('hello');}).then(result => result);​
​
const p2 = new Promise((resolve, reject) => {throw new Error('报错了');}).then(result => result);​
​
Promise.all([p1, p2]).then(result => console.log(result)).catch(e => console.log(e));// Error: 报错了

race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例

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

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变​
率先改变的 Promise 实例的返回值则传递给p的回调函数。

allSettled()
Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例​
只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

const promises = [fetch('/api-1'),fetch('/api-2'),fetch('/api-3'),];​
​
await Promise.allSettled(promises);removeLoadingIndicator();

resolve()
将现有对象转为 Promise 对象

Promise.resolve('foo')// 等价于​
new Promise(resolve => resolve('foo'))

参数可以分成四种情况,分别如下:​

  • 参数是一个 Promise 实例,promise.resolve将不做任何修改、原封不动地返回这个实例​
  • 参数是一个thenable对象,promise.resolve会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then()方法​
  • 参数不是具有then()方法的对象,或根本就不是对象,Promise.resolve()会返回一个新的 Promise 对象,状态为resolved​
  • 没有参数时,直接返回一个resolved状态的 Promise 对象

reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected​

const p = Promise.reject('出错了');// 等同于​
const p = new Promise((resolve, reject) => reject('出错了'))​
​
p.then(null, function (s) {​
  console.log(s)});// 出错了

Promise.reject()方法的参数,会原封不动地变成后续方法的参数

Promise.reject('出错了').catch(e => {​
  console.log(e === '出错了')})// true

三、使用场景

将图片的加载写成一个Promise,一旦加载完成,Promise的状态就发生变化

const preloadImage = function (path) {return new Promise(function (resolve, reject) {const image = new Image();​
    image.onload  = resolve;​
    image.onerror = reject;​
    image.src = path;});};

通过链式操作,将多个渲染数据分别给个then,让其各司其职。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题

// 各司其职​
getInfo().then(res=>{let { bannerList } = res​
    //渲染轮播图​
    console.log(bannerList)return res​
}).then(res=>{​
    ​
    let { storeList } = res​
    //渲染店铺列表​
    console.log(storeList)return res​
}).then(res=>{let { categoryList } = res​
    console.log(categoryList)//渲染分类列表​
    return res​
})

通过all()实现多个请求合并在一起,汇总所有请求结果,只需设置一个loading即可

function initLoad(){// loading.show() //加载loading​
    Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{​
        console.log(res)​
        loading.hide() //关闭loading​
    }).catch(err=>{​
        console.log(err)​
        loading.hide()//关闭loading​
    })}//数据初始化    ​
initLoad()

通过race可以设置图片请求超时

//请求某个图片资源​
function requestImg(){var p = new Promise(function(resolve, reject){var img = new Image();​
        img.onload = function(){resolve(img);}//img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg"; 正确的​
        img.src = "https://b-gold-cdn.xitu.io/v3/static/img/logo.a7995ad.svg1";});return p;}​
​
//延时函数,用于给请求计时​
function timeout(){var p = new Promise(function(resolve, reject){setTimeout(function(){reject('图片请求超时');}, 5000);});return p;}​
​
Promise​
.race([requestImg(), timeout()]).then(function(results){​
    console.log(results);}).catch(function(reason){​
    console.log(reason);});

http://www.kler.cn/a/390327.html

相关文章:

  • 怎么防止SQL注入攻击
  • c语言 --- 字符串
  • LabVIEW智能水肥一体灌溉控制系统
  • 基于ESP8266 wifimanager实现WiFi配置及天气显示
  • Python----Python高级(函数基础,形参和实参,参数传递,全局变量和局部变量,匿名函数,递归函数,eval()函数,LEGB规则)
  • 【某大型互联网企业】软件测试面试经验分享(1 ~ 3年)
  • 24/11/11 算法笔记 泊松融合
  • 开源项目低代码表单设计器FcDesigner扩展组件分组
  • 基于汇编语言实现的彩色黑白棋游戏
  • gitlab项目如何修改主分支main为master,以及可能遇到的问题
  • Electron 项目中获取 Windows 进程列表的深入剖析
  • Microsoft 365 Exchange如何设置可信发件IP白名单
  • MFC中 error C2440错误分析及解决方法
  • Google Go编程风格指南-介绍
  • 工业通信协议对比:OPC-UA、Modbus、MQTT、HTTP
  • The Input data type is inconsistent with defined schema
  • XHCI 1.2b 规范摘要(15)
  • 刷题统计(C语言)
  • 【Word2Vec】传统词嵌入矩阵训练方法
  • DataX任务:同步mysql数据到Elasticsearch,且Elasticsearch索引带有分词器
  • FPGA学习(10)-数码管
  • 工位管理新策略:Spring Boot企业级应用
  • 4-3-2.C# 数据容器 - Dictionary 扩展(Dictionary 存储对象的特性、Dictionary 与数组的转换)
  • 【爬虫分享】
  • PYTHON常用基础库-写算法
  • uni-app用户登录⑫