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

ES6中Map Set使用上的区别:

javascript 复制代码
//首先初始化数据​
var lng=100​
var 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,又译超编程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的数据,或者在运行时完成部分本应在编译时完成的工作​

一段代码来理解

javascript 复制代码
#!/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 实例

javascript 复制代码
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 实例本身,最后一个参数可选

javascript 复制代码
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能够对数组增删改查进行拦截,下面是试下你数组读取负数的索引

javascript 复制代码
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 不能修改该属性,否则会报错

javascript 复制代码
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的属性值符合要求.

javascript 复制代码
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方法将不起作用

javascript 复制代码
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,就会报错

javascript 复制代码
'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命令删除.

javascript 复制代码
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方法删除,否则报错;

取消代理

javascript 复制代码
Proxy.revocable(target, handler);

三、使用场景

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

  • 拦截和监视外部对对象的访问
  • 降低函数或类的复杂度
  • 在复杂操作前对操作进行校验或对所需资源进行管理
  • 使用 Proxy 保障数据类型的准确性
javascript 复制代码
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​

javascript 复制代码
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 代理,拦截赋值操作,触发充当观察者的各个函数

javascript 复制代码
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表达式,定义不同的内部状态
javascript 复制代码
function* helloWorldGenerator() {​
  yield 'hello';​
  yield 'world';​
  return 'ending';​
}

二、使用

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

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

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

javascript 复制代码
function* helloWorldGenerator() {​
  yield 'hello';​
  yield 'world';​
  return 'ending';​
}​
var hw = helloWorldGenerator();

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

  • 遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
  • 下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式
  • 如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
  • 如果该函数没有return语句,则返回的对象的value属性值为undefined
javascript 复制代码
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表达式的返回值.

javascript 复制代码
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进行遍历

javascript 复制代码
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
    这里通过文件读取案例,将几种解决异步的方案进行一个比较:

回调函数

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

javascript 复制代码
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就是为了解决回调地狱而产生的,将回调函数的嵌套,改成链式调用

javascript 复制代码
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函数非常适合将异步任务同步化

javascript 复制代码
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形式,更为简洁,语义化更强了

javascript 复制代码
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是异步解决的一种方案,最大特点则是将异步操作同步化表达出来

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

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

javascript 复制代码
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接口

javascript 复制代码
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 ,译为承诺,是异步编程的一种解决方案,比传统的解决方案(回调函数)更加合理和更加强大​

在以往我们如果处理多层异步操作,我们往往会像下面那样编写我们的代码

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

阅读上面代码,是不是很难受,上述形成了经典的回调地狱​

现在通过Promise的改写上面的代码

javascript 复制代码
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实例

javascript 复制代码
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能链式书写的原因

javascript 复制代码
getJSON("/posts.json").then(function(json) {​
  return json.post;​
}).then(function(post) {​
  // ...​
});

catch

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

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

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

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

一般来说,使用catch方法代替then()第二个参数​

Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应

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

浏览器运行到这一行,会打印出错误提示ReferenceError: x is not defined,但是不会退出进程​

catch()方法之中,还能再抛出错误,通过后面catch方法捕获到

finally()

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

javascript 复制代码
promise​
.then(result => {···})​
.catch(error => {···})​
.finally(() => {···});

构造函数方法

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

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

all()

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

javascript 复制代码
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方法

javascript 复制代码
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方法

javascript 复制代码
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 实例

javascript 复制代码
const p = Promise.race([p1, p2, p3]);

只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变​

率先改变的 Promise 实例的返回值则传递给p的回调函数。

allSettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例​

只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

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

resolve()

将现有对象转为 Promise 对象

kotlin 复制代码
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​

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

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

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

三、使用场景

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

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

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

javascript 复制代码
// 各司其职​
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即可

javascript 复制代码
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可以设置图片请求超时

javascript 复制代码
//请求某个图片资源​
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);​
});
相关推荐
学不会•41 分钟前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS2 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年5 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder6 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript