前端开发中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);​
});
相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅5 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊6 小时前
jwt介绍
前端
爱敲代码的小鱼6 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax