Promise
Promise
Promise 是一种用于异步编程的 JavaScript 对象,主要用于处理异步操作的结果
Promise 导致的问题
- 回调地狱(代码难以阅读)
- 错误处理(无法统一处理错误)
- 多个异步操作(“同步结果”困难)
- Promise 可以使用.then()方法链式处理异步逻辑
- Promise 可以使用.catch()方法处理异步操作失败
- Promise 提供.all()、.race()方法支持处理多个 Promise 对象逻辑
const promise = new Promise((resolve, reject) => {
  resolve('成功');
});
promise.then(
  (value) => {
    console.log('成功');
  },
  (reason) => {
    console.log('失败');
  }
);Promise 的链式调用
- 返回的是一个普通值(非 promise 的值)这个值会被传到外层 then 的下一个 then 的成功中去
- 没有返回值(抛错了),会执行外层的 then 的下一个 then 的失败
- 返回的是 promise ,会去解析返回的 promise 将解析后的值,传递到成功或者失败中(看这个 promise 是什么状态)
Promise 调用形式
其实他的本质就是在我们调⽤这些⽀持链式调⽤的函数的结尾时,他⼜返回了⼀个包含他⾃⼰的对象或者是⼀个新的⾃⼰,这些⽅式都可以实现链式调⽤。
var p = new Promise(function(resolve,reject){
  resolve('我是Promise的值')
})
console.log(p) p.then(function(res){
  // 该res的结果是resolve传递的参数
  console.log(res)
}).then(function(res){
  // 该res的结果是undefined
  console.log(res)
  return '123'
  }).then(function(res){
  // 该res的结果是123
  console.log(res)
  return new Promise(function(resolve){
  resolve(456)
  })
}).then(function(res){
  // 该res的结果是456
  console.log(res)
  return '我是直接返回的结果'
}).then()
  .then('我是字符串')
  .then(function(res){
  // 该res的结果是“我是直接返回的结果”
  console.log(res)
})根据现象我们可以分析出链式调⽤形式
- 只要有 then()并且触发了 resolve,整个链条就会执⾏到结尾,这个过程中的第⼀个回调函数的参数是 resolve 传⼊的值
- 后续每个函数都可以使⽤ return 返回⼀个结果,如果没有返回结果的话下⼀个 then 中回调函数的参数就是 undefined
- 返回结果如果是普通变量,那么这个值就是下⼀个 then 中回调函数的参数
- 如果返回的是⼀个 Promise 对象,那么这个 Promise 对象 resolve 的结果会变成下⼀次 then 中回调的函数的参数
- 如果 then 中传⼊的不是函数或者未传值,Promise 链条并不会中断 then 的链式调⽤,并且在这之前最后⼀次的返回结果,会直接进⼊离它最近的正确的 then 中的回调函数作为参数
中断链式
有两种方式可以中的 Promise 的链式,并触发 catch
var p = new Promise(function(resolve, reject){
  resolve('我是Promise的值')
})
console.log(p) p.then(function(res){
console.log(res)
}).then(function(res){
  // 有两种⽅式中断Promise
  // throw('我是中断的原因')
  return Promise.reject('我是中断的原因')
}).then(function(res){
  console.log(res)
}).then(function(res){
  console.log(res)
}).catch(function(err){
  console.log(err)
})then 函数虽然每次都返回 Promise 对象,来实现链式调⽤,但是 then 函数每次返回的都是⼀个新的 Promise 对象
var p = new Promise(function (resolve, reject) {
  resolve('我是Promise的值');
});
var p1 = p.then(function (res) {});
console.log(p);
console.log(p1);
console.log(p1 === p); // 输出 falsePromise 的方法
Promise.resolve()
Promise.resolve()会返回一个新的 Promise,可以解析传入的 Promise,具有等待效果
const promise = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, '完成');
});
Promise.resolve(promise).then((data) => {
  console.log(data);
});Promise.reject()
Promise.reject()返回一个已拒绝的 Promise
function resolved(result) {
  console.log('Resolved');
}
function rejected(result) {
  console.error(result);
}
Promise.reject(new Error('fail')).then(resolved, rejected);Promise.all()
代码中需要使⽤异步流程控制时,可以通过 Promise.then 来实现让异步流程⼀个接⼀个的执⾏,假设实际案例中,某个模块的⻚⾯需要同时调⽤ 3 个服务端接⼝,并保证三个接⼝的数据全部返回后,才能渲染⻚⾯。这种情况如果 a 接⼝耗时 1s、b 接⼝耗时 0.8s、c 接⼝耗时 1.4s,如果只⽤ Promise.then 来执⾏流程控制,可以保证三个接⼝按顺序调⽤结束再渲染⻚⾯,但是如果通过 then 函数的异步控制,必须等待每个接⼝调⽤完毕才能调⽤下⼀个,这样总耗时就是 1+0.8+1.4 = 3.2s。这种累加显然增加了接⼝调⽤的时间消耗,所以 Promise 提供了⼀个 all ⽅法来解决这个问题
Promise.all([promise对象,promise对象,...]).then(回调函数)
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2]).then((values) => {
  // values 为传入的Promise结果集合
  console.log(values); // [3, 'foo']
});Promise.race()
race ⽅法与 all ⽅法使⽤格式相同:
Promise.race([promise对象,promise对象,...]).then(回调函数)
回调函数的参数是前⾯数组中最快⼀个执⾏完毕的 promise 的返回值。所以使⽤ race ⽅法主要的使⽤场景是什么样的呢?举个例⼦,假设我们的⽹站有⼀个播放视频的⻚⾯,通常流媒体播放为了保证⽤户可以获得较低的延迟,都会提供多个媒体数据源。我们希望⽤户在进⼊⽹⻚时,优先展示的是这些数据源中针对当前⽤户速度最快的那⼀个,这时便可以使⽤ Promise.race()来让多个数据源进⾏竞赛,得到竞赛结果后,将延迟最低的数据源⽤于⽤户播放视频的默认数据源,这个场景便是 race 的⼀个典型使⽤场景。
const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
  // promise2 最先执行完成
  console.log(value); // 'two'
});Promise.prototype.finally()
finally 无论 Promise 成功还是失败都会执行
Promise.resolve().finally(() => {
  console.log('执行了');
});Generator
Generator 对象由生成器函数返回并且它符合可迭代协议和迭代器协议。
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}
const gen = generator(); // "Generator { }"
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3使用实例
const fs = require('fs/promises');
const path = require('path');
function* readResult() {
  const fileName = yield fs.readFile(
    path.resolve(__dirname, 'name.txt'),
    'utf8'
  );
  const age = yield fs.readFile(path.resolve(__dirname, 'age.txt'), 'utf8');
  return age;
}
function co(it) {
  return new Promise((resolve, reject) => {
    function next(data) {
      const { value, done } = it.next(data);
      if (!done) {
        Promise.resolve(value).then((data) => {
          next(data);
        }, reject);
      } else {
        resolve(value);
      }
    }
    next();
  });
}
co(readResult()).then((data) => {
  console.log(data);
});async/await
async 函数是使用 async 关键字声明的函数,并且其中允许使用 await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用 promise。
const fs = require("fs/promises");
const path = require("path");
async readResult() {
  const age = await fs.readFile(path.resolve(__dirname, "age.txt"), "utf8");
  return age
}事件环
进程
- 进程是系统进行分配资源的最小单位
- 每个进程都是独立的,但是可以通过通信做到进程间互相交互
浏览器是一个多进程模型,对浏览器而言,每个页签都是一个进程。好处就是安全,网页挂了之后不影响其他页签,隔离不能互相访问。
- js 主线程是单线程的
- 网络线程(接口请求)、事件触发线程(调度异步任务)、EventLoop(定时器、事件)是多线程
EventLoop
- 任务也是具备优先级的
- 宏任务:(浏览器提供)脚本执行,ui 渲染,定时器,请求,事件,MessageChannel,setImmediate(ie 独有)
- 微任务:(语言提供的就是微任务)promise.then,mutationObserver,queueMicrotask
- 默认会拿出一个宏任务执行,宏任务执行的时候会产生微任务,当宏任务执行完成后会清空所有的微任务
详见宏任务和微任务
