promise

127 阅读4分钟

实现一个promise

const Promise2 = function(constructor) {
  let self = this;
  self.status = 'pending';
  self.value = undefined;
  self.reason = undefined;
  function resolve(value) {
    if (self.status === 'pending') {
      self.value = value;
      self.status = 'resolved';
    }
  }
  function reject(reason) {
    if (self.status === 'pending') {
      self.reason = reason;
      self.status = 'rejected';
    }
  }
  try {
    constructor(resolve, reject);
  } catch(e) {
    reject(e);
  }
}
Promise2.prototype.then = function(onFulfilled, onRejected) {
  let self = this;
  switch(self.status) {
    case 'resolved':
      onFulfilled(self.value);
      break;
    case 'rejected':
      onRejected(self.reason);
      break;
    default:
  }
}
// let instance = new Promise2((resolve, reject) => resolve(1));
// instance.then(res => console.log(res));

实现Promise.resolve、Promise.reject

  • Promise.resolve(value) 方法返回一个以给定值解析后的Promise 对象。
  • 如果这个值是一个promise,那么将返回这个promise;
  • 如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。
  • Promise.reject() 方法返回一个带有拒绝原因的Promise对象。
Promise.resolve2 = value => {
  if (value && typeof value === "object" && (value instanceof Promise)) {
    return value;
  }
  return new Promise(resolve => {
    resolve(value);
  })
}
Promise.resolve2(2).then(console.log);

Promise.reject2 = value => {
  return new Promise((resolve, reject) => {
    reject(value);
  })
}
Promise.reject2(new Error('nishi shabi')).then(() => {
  console.log("resolved");
}, error => {
  console.log(error);
})

实现Promise.all

  • Promise.all 的返回值是一个新的 Promise 实例。
  • Promise.all 接受一个可遍历的数据容器,容器中每个元素都应是 Promise 实例。咱就是说,假设这个容器就是数组。
  • 数组中每个Promise实例都成功时(由pendding状态转化为fulfilled状态),Promise.all 才成功。这些 Promise 实例所有的 resolve 结果会按照原来的顺序集合在一个数组中作为 Promise.all 的 resolve 的结果。
  • 数组中只要有一个 Promise 实例失败(由pendding状态转化为rejected状态),Promise.all 就失败。Promise.all 的 .catch() 会捕获到这个 reject
Promise.all2 = function(promises = []) {
  if (!promises.length) { return Promise.resolve([]); }
  return new Promise((resolve, reject) => {
    let resAll = [];
    let len = promises.length;
    promises.forEach(item => {
      item.then(res => {
        resAll.push(res);
        if (resAll.length === len) {
          resolve(resAll);
        }
      }).catch(error => {
        reject(error);
      })
    });
  })
}
let P1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});
let P2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(2);
  }, 1500);
});
Promise.all2([P1, P2]).then(res => {
  console.log(res);
});

实现Promise.allSettled

  • 不管是全部成功还是有部分失败,最终都会进入Promise.allSettled的.then回调中;
  • 最后的返回值中,成功和失败的项都有status属性,成功时值是fulfilled,失败时是rejected;
  • 最后的返回值中,成功含有value属性,而失败则是reason属性;
Promise.allSettled2 = (promises = []) => {
  return new Promise((resolve, reject) => {
    let len = promises.length;
    let count = 0;
    let resAll = [];
    if (!len) {
      resolve([]);
    }
    promises.forEach((item, index) => {
      Promise.resolve(item).then(res => {
        count += 1;
        resAll[index] = {
          status: "fulfilled",
          value: res
        };
        if (count === len) {
          resolve(resAll);
        }
      }).catch(error => {
        count += 1;
        resAll[index] = {
          status: "rejected",
          reason: error
        };
        if (count === len) {
          resolve(resAll);
        }
      });
    })
  })
}
// 测试一下
const p1 = Promise.resolve(1)
const p2 = Promise.reject('err4')
const P = Promise.allSettled2([p1, p2]).then(res => {
  console.log(res);
});

实现一个Promise.race

Promise.race2 = (promises = []) => {
  return new Promise((resolve, reject) => {
    promises.forEach(item => {
      Promise.resolve(item).then(res => {
        resolve(res);
      }).catch(error => {
        reject(error);
      })
    });
  })
}

event loop执行顺序:

  1. 开始整个脚本作为一个宏任务执行;
  2. 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列;
  3. 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完;
  4. 执行浏览器UI线程的渲染工作;
  5. 检查是否有web worker任务,有则执行;
  6. 执行完本轮的宏任务,回到2,依次循环,直到宏任务和微任务全部队列为空;

微任务包括: MutationObserver、Promise.then()或catch()、Promise为基础开发的其他技术,比如fetch API、V8的垃圾回收过程、Node独有的process.nextTick。

宏任务包括: script、setTimeout、setInterval、setImmediate、I/O、UI rendering

注意: 在所有任务开始的时候,由于宏任务包括了script,所以浏览器会先执行一个宏任务,在这个过程中看到的延迟任务如setTimeout将放到下一轮宏任务执行

  console.log("start");

  setTimeout(() => {
    console.log("setTimeout1");
  }, 0);

  (async function foo() {
    console.log("async 1");

    await asyncFunction();

    console.log("async2");

  })().then(console.log("foo.then"));

  async function asyncFunction() {
    console.log("asyncFunction");

    setTimeout(() => {
      console.log("setTimeout2");
    }, 0);

    new Promise((res) => {
      console.log("promise1");

      res("promise2");
    }).then(console.log);
  }

  console.log("end");
  /*
  start
  async 1
  asyncFunction
  promise1
  foo.then
  end

  promise2
  async2
  
  setTimeout1
  setTimeout2
  
  **/

使用Promise实现红绿灯交替重复亮

红灯3秒亮一次,黄灯2秒亮一次,绿灯1秒亮一次;如何让三个灯不断交替重复亮灯?(用Promise实现)三个亮灯函数已经存在:

function red() {
  console.log("red");
}
function green() {
  console.log("green");
}
function yellow() {
  console.log("yellow");
}
const light = (cb, timer) => {
  return new Promise(resolve => {
    setTimeout(() => {
      cb();
      resolve();
    }, timer);
  })
}
async function setup() {
  await light(red, 3000);
  await light(green, 2000);
  await light(yellow, 1000);
  setup();
}
setup();

实现有并行限制的 Promise 调度器

题目描述:JS 实现一个带并发限制的异步调度器 Scheduler,保证同时运行的任务最多有两个
addTask(1000,"1");
addTask(500,"2");
addTask(300,"3");
addTask(400,"4");
的输出顺序是:2 3 1 4

整个的完整执行流程:
一开始1、2两个任务开始执行
500ms时,2任务执行完毕,输出2,任务3开始执行
800ms时,3任务执行完毕,输出3,任务4开始执行
1000ms时,1任务执行完毕,输出1,此时只剩下4任务在执行
1200ms时,4任务执行完毕,输出4

class Scheduler {
  constructor(limit) {
    this.queue = [];
    this.maxCount = limit;
    this.runCounts = 0;
  }
  add(time, order) {
    const promiseCreate = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(order);
          resolve();
        }, time);
      });
    }
    this.queue.push(promiseCreate);
  }
  request() {
    if (!this.queue.length || this.runCounts >= this.maxCount) {
      return;
    }
    this.runCounts++;
    // shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
    this.queue.shift()().then(() => {
      this.runCounts--;
      this.request();
    });
  }
  taskStart() {
    for (let i = 0; i < this.maxCount; i++) {
      this.request();
    }
  }
}
const scheduler = new Scheduler(2);
scheduler.add(1000, 1);
scheduler.add(500, 2);
scheduler.add(300, 3);
scheduler.add(400, 4);
scheduler.taskStart();