Promise

189 阅读6分钟

1.async await

//promise
// await 异步没有依存关系 一般就异步并行 
async function f1(){
  return await Promise.all([
    new Promise((resolve,reject)=>{
      setTimeout(() => {
        resolve(11);
      }, 1000);
    }),
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(22);
      }, 2000);
    })
  ])
}
f1().then((data)=>{
  console.log(data); //[11,22]
})

1)常见书写以及async await注意事项

  • await 命令后面的Promise对象,运行结果可能是 rejected,此时等同于 async 函数返回的 Promise 对象被reject。因此需要加上错误处理,可以给每个 await 后的 Promise 增加 catch 方法;也可以将 await 的代码放在 try...catch 中。
  • 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
  • await命令只能用在async函数之中,如果用在普通函数,会报错。
  • async 函数可以保留运行堆栈。
function b(){
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(22);
    }, 4000);
  });
};
function c(){
  return new Promise((resolve, reject) => {
    throw new Error('error0')
  });
}
const m=async ()=>{
  await b();
  c()
}
m(); //(node:7016) UnhandledPromiseRejectionWarning: Error: error0

2.如何实现 Promise.race?

  • Promise.race返回的仍然是一个Promise. 它的状态与第一个完成的Promise的状态相同。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个Promise是哪一种状态。
  • 如果传入的参数是不可迭代的,那么将会抛出错误。
  • 如果传的参数数组是空,那么返回的 promise 将永远等待。
  • 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。
Promise.race2 = function (promises) {
  //promises 必须是一个可遍历的数据结构,否则抛错
  return new Promise((resolve, reject) => {
    if (typeof promises[Symbol.iterator] !== 'function') {
      //真实不是这个错误
      Promise.reject('args is not iteratable!');
    }
    if (promises.length === 0) {
      return;
    } else {
      for (let i = 0; i < promises.length; i++) {
        Promise.resolve(promises[i]).then((data) => {
          resolve(data);
          return;
        }, (err) => {
          reject(err);
          return;
        });
      }
    }
  });
}
//一直在等待态
Promise.race2([]).then((data) => {
    console.log('success ', data);
}, (err) => {
    console.log('err ', err);
});
//抛错
Promise.race2().then((data) => {
    console.log('success ', data);
}, (err) => {
    console.log('err ', err);
});
Promise.race2([
    new Promise((resolve, reject) => { setTimeout(() => { resolve(100) }, 1000) }),
    new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 200) }),
    new Promise((resolve, reject) => { setTimeout(() => { reject(100) }, 100) })
]).then((data) => {
    console.log(data);
}, (err) => {
    console.log(err);
});

1)可遍历数据结构的有什么特点?

一个对象要具备可被for...of循环遍历的iterator接口,那么其[Symbol.iterator]属性必须是一个函数,在其部署遍历器生成方法(或者原型链上的对象具有该方法)

  • PS: 遍历器对象根本特征就是具有next方法。每次调用next方法,都会返回一个代表当前成员的信息对象,具有value和done两个属性。
//如为对象添加Iterator 接口;
let obj = {
    name: "Yvette",
    age: 18,
    job: 'engineer',
    [Symbol.iterator]() {
        const self = this;
        const keys = Object.keys(self);
        let index = 0;
        return {
            next() {
                if (index < keys.length) {
                    return {
                        value: self[keys[index++]],
                        done: false
                    };
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
};

for(let item of obj) {
    console.log(item); //Yvette  18  engineer
}

返回遍历器对象,具备可被for...of循环遍历的iterator接口

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象
  • ES6 的数组、Set、Map 都部署了以下三个方法: entries() / keys() / values(),调用后都返回遍历器对象。

3.如何实现Promise.all()

  • 如果传入的参数是一个空的可迭代对象,那么此promise对象回调完成(resolve),只有此情况,是同步执行的,其它都是异步返回的。
  • 如果传入的参数不包含任何 promise,则返回一个异步完成. promises 中所有的promise都“完成”时或参数中不包含 promise 时回调完成。
  • 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败
  • 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
Promise.all5 = function (promises) {
  return new Promise((resolve, reject) => {
    let index = 0;
    let result = [];
    if (promises.length === 0) {
      resolve(result);
    } else {
      function processValue(i, data) {
        result[i] = data;
        if (++index === promises.length) {
          resolve(result);
        }
      }
      for (let i = 0; i < promises.length; i++) {
        //promises[i] 可能是普通值
        Promise.resolve(promises[i]).then((data) => {
          processValue(i, data);
        }, (err) => {
          reject(err);
          return;
        });
      }
    }
  });
}

Promise.all5([11,22]).then((data)=>{
  console.log(data); 
})

4.插入Number 实现a==1 && a==2 && a==3

// number
var name=Symbol();
var obj={
  name:'AliceLee',
  age:24,
  [name]: 'AliceLee1',
}

// 法一
// 实现a==1 && a==2 && a==3
var a = {
  i: 1,
  toString: function () {
    return this.i++;
  }
}
console.log(a == 1 && a == 2 && a == 3);

//法二
let value = 1;
Object.defineProperty(window, 'a', {
  get: function () {
    return value++
  }
})
console.log(a == 1 && a == 2 && a == 3);

//法三
var reg=/\d/g;
var a={
  reg:/\d/g,
  valueOf:function(){
    return reg.exec(123)[0];
  }
}
console.log(a == 1 && a == 2 && a == 3);

//法四
var a={
  valueOf: (function () { 
    var i= 1;
    return function(){
      return i++;
    }
  })()
}
console.log(a == 1 && a == 2 && a == 3);

// parseInt('0111'); //111
// parseInt(0111); //八进制数 73

5. 插入Object.defineProperty与proxy

  • Object.defineProperty
let arry = ['travel', 'reading'];
Object.defineProperty(arry, '0', {
  get() {
    console.log("读取成功");
    return temp
  },
  set(value) {
    console.log("设置成功");
    temp = value;
  }
});

arry[0] = 10; //触发设置成功
arry.push(10); //不能被劫持
  • proxy
let hobbits = ['travel', 'reading'];
let p = new Proxy(hobbits, {
  get(target, key) {
    //if(key === 'length') return true; //如果是数组长度的变化,返回。
    console.log('读取成功');
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    //if(key === 'length') return true; //如果是数组长度的变化,返回。
    console.log('设置成功');
    return Reflect.set([target, key, value]);
  }
});
p.splice(0, 1) //触发get和set,可以被劫持
p.push('photography'); //触发get和set
p.slice(1); //触发get;因为 slice 是不会修改原数组的

6.执行顺序

console.log('script start');

setTimeout(function () {
    console.log('setTimeout---0');
}, 0);

setTimeout(function () {
    console.log('setTimeout---200');
    setTimeout(function () {
        console.log('inner-setTimeout---0');
    });
    Promise.resolve().then(function () {
        console.log('promise5');
    });
}, 200);

Promise.resolve().then(function () {
    console.log('promise1');
}).then(function () {
    console.log('promise2');
});
Promise.resolve().then(function () {
    console.log('promise3');
});
console.log('script end');

script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0

7.this 的执行顺序

var number = 1;
var obj = {
  'number': 4,
  'db1': (function () {
    this.number *= 2;
    return function () {
      this.number *= 2
    }
  })()
}
var db1 = obj.db1;
db1()
obj.db1()
console.log(number)        //4
console.log(obj.number)    //8
console.log(number + obj.number)  //12
var number = 5;
var obj = {
    number: 3,
    fn: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var myFun = obj.fn;
myFun.call(null);
obj.fn();
console.log(window.number);

10
9
3
27
20

8. 函数柯理化

function curry(fn, args = []) {
    return function(){
        let rest = [...args, ...arguments];
        if (rest.length < fn.length) {
            return curry.call(this,fn,rest);
        }else{
            return fn.apply(this,rest);
        }
    }
}
//test
function sum(a,b,c) {
    return a+b+c;
}
let sumFn = curry(sum);
console.log(sumFn(1)(2)(3)); //6
console.log(sumFn(1)(2, 3)); //6

9.深拷贝 考虑循环引用的问题 用WeakMap()


function deepClone(obj,hash=new WeakMap()){
  if (obj instanceof RegExp) return new RegExp(obj);
  if (obj instanceof Date) return new Date(obj);
  if(hash.has(obj)){
    return hash.get(obj);
  }
  var res=Array.isArray(obj)?[]:{};
  hash.set(obj,res);
  for(var key in obj){
    if(obj.hasOwnProperty(key)){
      res[key] = typeof obj[key] === 'object' ? deepClone(obj[key],hash) : obj[key];
    }
  }
  return res;
};

var a=Symbol();
var obj = {
  name: 'st',
  age: 18,
  hobbies: ['reading', 'photography'],
  [a]:24,
  b:function(){
    return 0
  },                  //被忽略
  time:new Date(),
  reg:/\d/g,         //{}
  sex:undefined,    //被忽略  
  // 原型链上的属性无法获取
}


obj.circle = obj;//Maximum call stack size exceeded 循环引用自身
var newObj = deepClone(obj);
obj.hobbies.push('st');
console.log(obj, newObj);
输出:
{ name: 'st',
  age: 18,
  hobbies: [ 'reading', 'photography' ],
  b: [Function: b],
  time: 2019-09-06T03:05:04.122Z,
  reg: /\d/g,
  sex: undefined,
  circle: [Circular] }

1)浅拷贝

var newObj = Object.assign({}, obj);  //浅拷贝
var obj1 = { ...obj };  //浅拷贝
for(var key in obj){}  //浅拷贝
Array.prototype.slice() //浅拷贝
Array.prototype.concat() //浅拷贝

10.函数柯理化

// 通用柯理化
const currying=(fn,...args)=>{
  return args.length < fn.length ? (...arguments) => currying(fn, ...args, ...arguments) : fn(...args);
};
function sumFn(a, b, c) {
  return a + b + c;
};
var sum = currying(sumFn);
console.log(sum(2,3,5));
console.log(sum(2,3)(5));
console.log(sum(2)(3)(5));