Promise

ES6 Promise => 新版异步编程解决方案;回调函数 => 旧版异步编程解决方案

kongmingLantern
kongmingLantern

快速上手

实例对象是 new 函数产生的对象;函数对象是将函数作为对象使用。

回调地狱 => 回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件。

构造函数 Promise 主要用作包装返回值不是 Promise 的函数。可包装异步操作。

实例化 Promise 需传入函数类型的 executor。

executor 接受两个函数 resolutionFunc and rejectionFunc 作为参数。

executor 的返回值会被忽略。

在 executor 中抛出错误会返回 rejected 状态的 promise object。

function resolutionFunc(value) { console.log(value); }
function rejectionFunc(reason) { console.log(reason); }
let excutor = function (resolutionFunc, rejectionFunc) {
  setTimeout(resolutionFunc("yes"), 1000);
  // setTimeout(rejectionFunc("err"), 1000);
  // throw new Error('nono');
  return "ok";
};
let p = new Promise(excutor);
console.log(p);
p.then(
  (value) => { // 接收得到成功的 value 数据 => onResolved
    console.log("成功的回调", value);
  },
  (reason) => { // 接收得到失败的 reason 数据 => onRejected
    console.log("失败的回调", reason);
  }
);

resolutionFunc 和 rejectionFunc 通常会直接以 resolve 和 reject 表示。

resolve 或 reject 函数的调用,会将期约对象的状态设置为成功或失败。

const p = new Promise((resolve, reject) => { // 执行器函数 => 同步任务
  // 执行异步操作任务
  setTimeout(() => {
    const time = Date.now() 
    if (time % 2 == 0) { // 当前时间是偶数代表成功
      resolve('成功的数据,time=' + time)
    } else {
      reject('失败的数据,time=' + time)
    }
  }, 1000);
})

p.then(
  value => { // 接收得到成功的 value 数据 => onResolved
    console.log('成功的回调', value)
  },
  reason => { // 接收得到失败的 reason 数据 => onRejected
    console.log('失败的回调', reason)
  }
)

Promise 实例对象属性

[[Prototype]]: Promise
  catch: ƒ catch()
  constructor: ƒ Promise()
  finally: ƒ finally()
  then: ƒ then()
  Symbol(Symbol.toStringTag): "Promise"
  [[Prototype]]: Object
    all: ƒ all()
    allSettled: ƒ allSettled()
    any: ƒ any()
    length: 1
    name: "Promise"
    prototype: Promise {Symbol(Symbol.toStringTag): 'Promise', constructor: ƒ, then: ƒ, catch: ƒ, finally: ƒ}
    race: ƒ race()
    reject: ƒ reject()
    resolve: ƒ resolve()
    Symbol(Symbol.species): ƒ Promise()
    Symbol(Symbol.species): ƒ Promise()
    arguments: (...)
    caller: (...)
    [[Prototype]]: ƒ ()
    [[Scopes]]: Scopes[0]
[[PromiseState]]: "fulfilled(resolved)"|"pending"|"rejected"
[[PromiseResult]]: "{}"
  • 期约状态 PromiseState => 期约实例的属性

期约执行过程中的几种状态
pending => 异步任务进行中且未有结果
resolved => 异步任务执行完毕并回传 resolve 的结果
rejected => 异步任务执行失败并回传 reject 的结果

PromiseState 的改变只有两种可能,pending 变为 fulfilled|resolved 或 rejected。

期约状态的改变只有一次,不论成功与否都会返回结果数据 value or reason。

  • 期约结果值 PromiseResult => 期约实例的属性

PromiseResult 存储异步任务成功或失败的结果。

PromiseResult 可通过 resolve() 或 reject() 对其进行修改。

PromiseResult 中的值可在后续的 then() 方法回调中获取。

var p = new Promise((resolve,reject) => {resolve("async ok"),reject("async failed")})
console.log(p)
p.then((value)=>{console.log("resolved + "+value)},(reason)=>{console.log("rejected + "+reason)})
Promise{<fulfilled>: undefined}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: "async ok"
resolved + async ok
  • Promise 基本流程

通过 new 关键字和 Promise 构造器创建期约对象。

构造函数接受名为 executor 的函数参数,在其内部封装异步操作。

函数 executor 接受两个函数作为参数。异步任务成功时,函数 resolve 被调用,返回一个值代表成功;异步任务失败时,函数 reject 被调用,返回失败的原因,通常是一个 error 对象。

后续异步执行的 then 方法根据期约的状态选择调用成功或是失败的回调函数。

定义在原型对象 Promise.prototype 上的 then 方法返回一个 Promise Instance。

  • Promise 构造函数 => Promise (excutor) {}

Promise 实例化时接受函数类型的参数,可用箭头函数或匿名函数声明,这个函数有两个形参 resolve 和 reject。

executor 执行器函数会在 Promise 内部立即同步调用,异步操作在执行器执行。

  • Promise.prototype.then(onResolved, onRejected) => 指定得到 value 的成功回调或得到 reason 的失败回调,返回一个新 Promise Instance

onResolved、onRejected => then、catch 方法接受的两个参数,都是函数。

返回的新 Promise Instance 由 then 方法指定的回调函数决定。

then 中回调函数抛出异常 => Promise Instance 状态为 rejected,reason 为抛出的异常;

then 中回调函数返回的是非期约的任意值 => Promise Instance 状态为 resolved & fulfilled,value 为返回的值;

then 中回调函数返回的是期约实例 => 此期约实例的结果为 Promise Instance 的结果。

  • Promise.prototype.catch(onRejected) => 只用来指定失败的回调
// 内部也是由 then 实现 => Promise.prototype.then(undefined, onRejected)
// catch 函数的参数 => (onRejected) => {}
var p = new Promise((resolve,reject) => { reject("error"); })
p.catch((reason) => {console.log("catch func test => " + reason)})

Promise 函数对象方法

  • Promise.resolve() => 返回一个给定值解析的成功/失败的 promise 对象

若传入参数是非 Promise 的对象,则返回的结果都为一个成功的 Promise 对象。

let p = Promise.resolve("ok"),pp = Promise.resolve(false),ppp = Promise.resolve(22);
console.log(p,pp,ppp); // Promise {<fulfilled>: 'ok'} Promise {<fulfilled>: false} Promise {<fulfilled>: 22}

如果传入参数为 Promise 对象,则将返回这个 Promise 对象。

let p = Promise.resolve(new Promise((resolve,reject) => {resolve("ok")})),pp = Promise.resolve(new Promise((resolve,reject) => {reject("failed")}));
console.log(p,pp); // Promise {<fulfilled>: 'ok'} Promise {<rejected>: 'failed'}
pp.catch(err => console.log(err)); // failed

若参数是 thenable 对象,返回的 Promise 对象会跟随这个 thenable 的最终状态。

  • Promise.reject() => 返回一个失败的 Promise 对象

不论传入什么类型的参数,都会作为失败的结果返回一个失败的 Promise 对象。

let p = Promise.reject("no"),pp = Promise.reject(true),ppp = Promise.reject(22),pppp = Promise.reject(new Promise((resolve,reject) => {resolve("ok")}));
p.catch((err)=>{console.log(err)}); // no
pp.catch((err)=>{console.log(err)}); // true
ppp.catch((err)=>{console.log(err)}); // 22
pppp.catch((err)=>{console.log(err)}); // Promise {<fulfilled>: 'ok'}
  • Promise.all() => 返回一个新 Promise,只有所有传参期约都成功才成功

接受的参数通常为多个期约组成的数组,数组状态决定返回状态。新返回期约成功的结果是数组中每一个期约成功结果组成的数组,失败的结果是在传参期约数组中首个失败期约的失败结果。

// 测试成功情况
let p = new Promise((resolve,reject) => {resolve("OK")}),pp = Promise.resolve("Success"),ppp = Promise.resolve("zs");
console.log(Promise.all([p,pp,ppp])); // Promise {<pending>} [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: Array(3)
// 测试失败情况
let p = new Promise((resolve,reject) => {reject("No")}),pp = Promise.reject("NoNo"),ppp = Promise.resolve("zs");
let res = Promise.all([p,pp,ppp]);
console.log(res); // Promise {<pending>} [[Prototype]]: Promise [[PromiseState]]: "rejected" [[PromiseResult]]: "No"
res.catch(err => console.log(err)) // No
  • Promise.race() => 返回一个新的 Promise,取决于期约完成速度

接受的参数通常为多个期约组成的数组,数组第一个完成的期约结果状态就是最终的结果状态。

let p = new Promise((resolve,reject) => {resolve("OK")}),pp = Promise.resolve("Success"),ppp = Promise.resolve("zs");
console.log(Promise.race([p,pp,ppp])); // Promise {<pending>} [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: "OK"

期约相关理解

  • 改变期约状态和执行回调函数的先后顺序(resolve 和 then 谁先执行)?

改变 Promise 对象状态的方式有 resolve、reject 或 throw "xxx";执行回调函数是通过 then 或 catch。

都有可能。当执行器函数是一个同步任务时,先进行状态改变,即先完成 resolve 或 reject,再进行回调函数的调用。当执行器函数包裹异步任务时,改变状态需要等待一段时间,回调函数会先执行。在多数的开发场景中,后者情况居多。

在后者条件下若是硬性需要先状态改变再执行回调,那么可以通过定时器推迟时间去调用回调函数。

  • 获取到数据的时机 => 回调函数调用的时机

先指定回调,再改变状态,会在回调函数得到调用时取到数据;先改变状态,那么指定回调时,回调函数就会调用得到数据。

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("Ok");
  },1000)
})
console.log(p)
setTimeout(()=>{
  p.then(value => {
    console.log(value);
  },reason => {console.log(reason)})
},1000)
  • Promise 串连多个操作任务 => then 的链式调用串连多个同步/异步任务

then 返回的期约结果由其回调函数的返回值决定,若回调函数无返回值,那么默认为 undefined。

let p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok");
  }, 1000);
});
p.then((value) => {
  return new Promise((resolve, reject) => {
    resolve("Success");
  });
}) // 整体返回看作期约
  .then((value) => {
    console.log(value); // Success
  }) // 整体返回看作期约
  .then((value) => {
    console.log(value); // undefined
  });
  • Promise 异常传透 => 使用 then 链式调用时,可在最后指定失败的回调

Promise 进行链式调用时,在中间环节不需要指定失败的回调,仅需在链的最后进行指定失败的回调即可。前面的任何操作出了异常,都会传到最后失败的回调中处理。

let p = new Promise((resolve, reject) => {
  setTimeout(() => { resolve("ok"); }, 1000);
});
p.then((value) => { throw "Oops, Error1" })
 .then((value) => { throw "Oops, Error2" })
 .then((value) => { console.log("Done_") })
 .catch(err => console.log(err)); // Oops, Error1
  • 中断 Promise 链 => 回调函数中返回一个 pendding 状态的 Promise 对象

Promise 链中断导致不再调用后面的回调函数,通过待办状态的期约对象实现。

let p = new Promise((resolve, reject) => {
  setTimeout(() => { resolve("ok"); }, 1000);
});
p.then((value) => { console.log("pendding"); return new Promise(() => {}) }) // pendding
 .then((value) => { throw "Oops, Error2" })
 .then((value) => { console.log("Un Done_") })
 .catch(err => console.log(err));
  • Promise Chain

期约对象的 then 方法可以返回一个新的期约对象;下一个 then 方法的参数是上一个 promise 对象的 resolve 参数;catch 方法的参数是其之前某个期约对象的 reject 参数。

当期约实例的状态改为 rejected 时,promise 会直接跳过 then 而直接执行 catch。

catch 方法里面依旧可以返回一个新的 promise 对象。

自定义 Promise

ES5 Function Version

  • 定义整体结构

构造函数传入 excutor 函数;原型方法的 then 接收两个函数参数;原型方法 catch 是 then(null, onRejected) 的语法糖;原型方法与函数对象方法;暴露构造函数。

(function (window) {
  function Promise(excutor) {}
  Promise.prototype.then = function (onResolved, onRejected) {};
  Promise.prototype.catch = function (onRejected) { }
  Promise.resolve = function (value) { }
  /*返回一个指定了失败 reason 的 promise 对象 */
  Promise.reject = function (reason) { }
  /*返回一个 promise, 只有 promises 中所有 promise 都成功时, 才最终成功, 只要有一个失败就直接 失败*/
  Promise.all = function (promises) { }
  /*返回一个 promise, 一旦某个 promise 解决或拒绝, 返回的 promise 就会解决或拒绝。 */
  Promise.race = function (promises) { }
  // 暴露构造函数
  window.Promise = Promise
})(window);
  • Promise 构造函数的实现

executor 是外部定义,内部同步执行的函数,通常其内部执行一些异步操作;

promiseState 初始状态为 pending;

promiseResult 是用来保存成功 value 或失败 reason 的属性;

用来保存所有待调用的包含 onResolved 和 onRejected 回调函数的对象的数组

function ZPromise(excutor) {
  const promiseInstance = this;
  promiseInstance.promiseState = "pending";
  promiseInstance.promiseResult = undefined;
  promiseInstance.callbacks = [];
  function resolve(value) {
    if (promiseInstance.status !== "pending") return;
    promiseInstance.promiseState = "resolved";
    promiseInstance.promiseResult = value;
    if (promiseInstance.callbacks.length > 0) {
      setTimeout(() => {
        promiseInstance.callbacks.forEach((obj) => {
          obj.onResolved(value);
        });
      });
    }
  }
  function reject(reason) {
    if (promiseInstance.promiseState !== "pending") return;
    promiseInstance.promiseState = "rejected";
    promiseInstance.promiseResult = reason;
    setTimeout(() => {
      promiseInstance.callbacks.forEach((obj) => {
        obj.onRejected(reason);
      });
    });
  }
  try {
    excutor(resolve, reject);
  } catch (error) {
    reject(error);
  }
}
  • promise.then()/catch()的实现

若 onResolved or onRejected 不是函数,可指定一个默认的函数,返回的结果据成功或失败状态而定。

ZPromise.prototype.then = function (onResolved, onRejected) {
  const promiseInstance = this;
  onResolved =
    typeof onResolved === "function" ? onResolved : (value) => value;
  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : (reason) => {
          throw reason;
        };
  return new Promise((resolve, reject) => {
    function handle(callback) {
      try {
        const x = callback(promiseInstance.promiseResult);
        if (x instanceof Promise) {
          x.then(resolve, reject);
        } else {
          resolve(x);
        }
      } catch (error) {
        reject(error);
      }
    }
    if (promiseInstance.promiseState === "resolved") {
      setTimeout(() => {
        handle(onResolved);
      });
    } else if (promiseInstance.promiseState === "rejected") {
      setTimeout(() => {
        handle(onRejected);
      });
    } else {
      promiseInstance.callbacks.push({
        onResolved(value) {
          handle(onResolved);
        },
        onRejected(reason) {
          handle(onRejected);
        },
      });
    }
  });
};
Promise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected);
};
  • Promise.resolve() & reject() 的实现

resolve 返回指定成功 value 的 promise 对象,value 通常为一般数据或 promise;

reject 返回指定失败 reason 的 promise 对象,reason 通常为一般数据或 error。

ZPromise.resolve = function (value) {
  return new ZPromise((resolve, reject) => {
    if (value instanceof ZPromise) {
      value.then(resolve, reject);
    } else {
      resolve(value);
    }
  });
};
ZPromise.reject = function (reason) {
  return new ZPromise((resolve, reject) => {
    reject(reason);
  });
};
  • Promise.all() & race() 的实现

Promise.all() 返回一个新的 promise 对象,只有 promises 中所有 promise 都产生成功 value 时, 才是最终成功,但凡有一个失败就直接失败。

ZPromise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let resolvedCount = 0;
    const promisesLength = promises.length;
    const values = new Array(promisesLength);
    for (let i = 0; i < promisesLength; i++) {
      Promise.resolve(promises[i]).then(
        (value) => {
          values[i] = value;
          resolvedCount++;
          if (resolvedCount === promisesLength) {
            resolve(values);
          }
        },
        (reason) => {
          reject(reason);
        }
      );
    }
  });
};
ZPromise.race = function (promises) {
  return new Promise((resolve, reject) => {
    for (var i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(
        (value) => {
          resolve(value);
        },
        (reason) => {
          reject(reason);
        }
      );
    }
  });
};

ES6 Class Version

class ZPromise {
  constructor(executor) {
    // 添加属性 => [[]] 双括号是内置属性
    this.promiseState = "pending";
    this.promiseResult = null;
    this.callbacks = [];
    let that = this;
    function resolve(arg) {
      // 状态只能修改一次 => 判断
      if (that.promiseState !== "pending") return;
      // 修改对象状态、设置对象结果值 => promiseState、promiseResult
      that.promiseState = "fulfilled"; // or resolved
      that.promiseResult = arg;
      // 调用成功的回调
      // if(that.callback.onResolved){ that.callback.onResolved(arg); } // 旧版本 => 不支持多个回调的实现
      setTimeout(() => {
        that.callbacks.forEach((item) => {
          item.onResolved(arg);
        });
      });
    }
  }
  then(onResolved, onRejected) {
    const that = this; // this 指向实例对象
    // 判断回调函数参数
    if (typeof onRejected !== "function") {
      // 通过默认值解决异常穿透
      onRejected = (reason) => {
        throw reason;
      };
    }
    if (typeof onResolved !== "function") {
      onResolved = (value) => value;
    }
    return new Promise((resolve, reject) => {
      // 封装复用代码
      function callbackF(type) {
        try {
          // 获取回调函数的执行结果
          let thenRes = type(that.promiseResult);
          if (thenRes instanceof Promise) {
            // 回调函数返回值若是 Promise 类型对象
            thenRes.then(
              (v) => {
                resolve(v);
              },
              (r) => {
                reject(r);
              }
            );
          } else {
            // then 方法返回结果的对象状态为成功 => then 方法成功回调的返回值不是一个 Promise 而是字符串、数字等...
            resolve(thenRes);
          }
        } catch (error) {
          reject(error);
        }
      }
      // 判断调用具体的回调
      if (this.promiseState === "fulfilled") {
        setTimeout(() => {
          callbackF(onResolved);
        });
      }
      if (this.promiseState === "rejected") {
        setTimeout(() => {
          callbackF(onRejected);
        });
      }
      // 处理异步任务的回调执行 => 回调执行不再 then 方法, 需要保存出去在 resolve 和 reject 中使用
      if (this.promiseState === "pending") {
        // 保存回调函数
        this.callbacks.push({
          // onResolved: onResolved,
          onResolved: function () {
            callbackF(onResolved);
          },
          // onRejected // ES6
          onRejected: function () {
            callbackF(onRejected);
          },
        });
      }
    });
  }
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }
  static resolve(value) {
    return new Promise((resolve, reject) => {
      if (value instanceof Promise) {
        value.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      } else {
        resolve(value);
      }
    });
  }
  static reject(reason) {
    return new Promise((resolve, reject) => {
      reject(reason);
    });
  }
  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0,
        arr = [];
      promises.forEach((item, index) =>
        item.then(
          (v) => {
            count++;
            arr[index] = v;
            if (count === promises.length) {
              resolve(arr);
            }
          },
          (r) => {
            reject(r);
          }
        )
      );
    });
  }
  static race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((item, index) => {
        item.then(
          (v) => {
            resolve(v);
          },
          (r) => {
            reject(r);
          }
        );
      });
    });
  }
}

async 与 await

async 函数

async 函数的返回值为 Promise 对象,期约对象的结果由被声明函数执行的返回值决定。若返回值是非期约类型的数据,那么这个数据会作为期约的结果值;若返回的是一个期约对象,那么返回结果决定了 async 函数返回结果的状态。同 then。

async function main() {}
console.log(main()); // [[Prototype]]: Promise [[PromiseState]]: "fulfilled" [[PromiseResult]]: undefined
async function main() { return "ok" }
console.log(main()); // Promise {<fulfilled>: 'ok'}
async function main() { return new Promise((resolve,reject) => {reject("Failed")}) }
console.log(main()); // Promise {<rejected>: 'Failed'}
async function main() { return new Promise((resolve,reject) => {throw "Oops, No"}) }
console.log(main()); // Promise {<rejected>: "Oops, No"}

await 表达式

await 右侧的表达式一般为 Promise 对象,也可以是其它值。若表达式是 Promise 对象,await 返回的是 Promise 成功的值;若表达式是其它值,则直接将此值作为 await 的返回值。

// await 右侧 不为 Promise
async function main() { let res1 = await "ok", res2 = await 1, res3 = await false; console.log(res1, res2, res3); }
main(); // ok 1 false
// await 右侧为成功的 Promise
async function main() {
  let p = new Promise((resolve, reject) => {
    resolve("Okk");
  });
  let res = await p;
  console.log(res); // Okk
}
main();
// await 右侧为失败的 Promise
async function main() {
  let p = new Promise((resolve, reject) => {
    reject("Failed") // throw "Oops, No";
  });
  try {
    let res = await p;
  } catch (error) {
    console.log(error); // 在此拿到失败结果
  }
}
main(); // Failed

使用规范

await 必在 async 函数中,但 async 函数中可以没有 await。如果 await 的 Promise 失败,那么会抛出异常,需要通过 try...catch 捕获处理。

// await 单独使用的异常
Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules
const fs = require("fs");
const path = require('path')
// 回调函数方式
fs.readFile(path.join(__dirname,"/resource/01.txt"),(err,data01)=>{
    if(err) throw err;
    fs.readFile(path.join(__dirname,"/resource/02.txt"),(err,data02)=>{
        if(err) throw err;
        fs.readFile(path.join(__dirname,"/resource/03.txt"),(err,data03)=>{
            if(err) throw err;
            console.log((data01+data02+data03).toString());
        })
    })
})
// async & await 方式
const util = require('util');
const mineReadFile = util.promisify(fs.readFile);
async function main(){
  try {
    let data01 = await mineReadFile(path.join(__dirname,"/resource/01.txt"));
    let data02 = await mineReadFile(path.join(__dirname,"/resource/02.txt"));
    let data03 = await mineReadFile(path.join(__dirname,"/resource/03.txt"));
    console.log((data01+data02+data03).toString());
  } catch (error) {
    console.log(error);
  }
}
main();
function promiseAjax(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.onreadystatechange = () => {
      if (xhr.readyState !== 4) return;
      const { status, response } = xhr;
      // 请求成功 => 调用 resolve(value)
      if (status >= 200 && status < 300) {
        resolve(JSON.parse(response));
      } else {
        // 请求失败 => 调用 reject(reason)
        reject(new Error("请求失败: status: " + status));
      }
    };
    xhr.open("GET", url);
    xhr.send();
  });
}
// 回调函数方式
promiseAjax("https://v2.jokeapi.dev/joke/Any?safe-mode").then(
  (data) => {
    console.log("显示成功数据", data);
  },
  (error) => {
    alert(error.message);
  }
);
// async & await 方式
async function getRes() {
  try {
    let res = await promiseAjax("https://v2.jokeapi.dev/joke/Any?safe-mode");
    console.log(res);
  } catch (error) {
    console.log(error);
  }
}
getRes();

async 和 await 本质上是生成器。* => async;yield => await。

EventLoop

事件循环机制用于浏览器和 Node 处理 JavaScript。单线程的 JavaScript 可有效避免因引入锁而带来的复杂化。

Сall Stack and Execution Stack are different names for the same thing, which is a LIFO stack used to store execution contexts created during code execution.

解释器会将调用的函数推入调用栈并开始执行。若调用栈中执行的函数调用其他的函数,新函数将被添加到调用栈,并立即执行。当前函数执行完毕后,解释器会将其执行上下文推出调用栈,继续执行余下代码。

Async & Sync

异步使程序可以在执行可能长期运行的任务时,继续对其他事件做出反应而不必等待任务完成。

同步回调立即执行,不放入回调队列 => 数组遍历相关的回调函数 & Promise 构造函数接收的 excutor。

异步回调不会立即执行,放入回调队列 => 定时器 & ajax & Promise 的成功 or 失败的回调。

异步任务的执行首先是进入调用栈发起调用,然后解释器将其响应回调任务放入任务队列,紧接着调用栈会将这个任务移除。当主线程清空后,即所有同步任务结束后,解释器会读取任务队列,并依次将已完成的异步任务加入调用栈中并执行。

异步任务的回调不是直接进入到任务队列,而是应等待执行到异步任务的回调函数时,才将其推入到任务队列中。

console.log('script start:sync code');
setTimeout(function(){console.log('setTimeout:100')},100); 
setTimeout(function(){console.log('setTimeout:0-1')},0); 
setTimeout(function(){console.log('setTimeout:0-2')},0);
console.log('script middle:sync code');
new Promise(function(resolve){
  console.log('promise 1:sync code')
  setTimeout(()=>{
    resolve()
  }, 10)   
}).then(function(){
    console.log('promise:setTimeou10') 
}); 
new Promise(function(resolve){
  console.log('promise 2:sync code')
  setTimeout(()=>{
    resolve()
  }, 0)    
}).then(function(){
  console.log('promise:setTimeou0') 
}); 
console.log('script end:sync code');

代码分析:解释器会将整体代码放入宏任务队列,同步代码运行输出后,即调用栈清空,该宏任务被推出宏任务队列。此时微任务队列里为空,宏任务队列里存在零秒定时的定时器回调。

// 同步代码运行输出
script start:sync code
script middle:sync code
promise 1:sync code
promise 2:sync code
script end:sync code
// 宏任务队列
console.log('setTimeout:0-1')
console.log('setTimeout:0-2')
()=>{resolve()}

代码分析:微任务队列已清空,执行下一个宏任务。在执行第三个零秒定时的定时器回调时,往微任务队列添加期约回调。在清空微任务队列里的函数后,再进行下一个宏任务,也就是依次十毫秒定时器回调和一百毫秒定时器回调。

宏队列与微队列

任务队列 => 存储待执行回调函数的队列

宏队列 => 保存待执行的宏任务回调 -> 定时器、DOM 事件、ajax 回调、整体代码块 script
微队列 => 保存待执行的微任务回调 -> Promise 回调、MutationObserver 回调

从宏任务队列中按入队顺序,将首个执行的宏任务放入调用栈开始执行;在执行完该宏任务下的所有同步任务后,调用栈清空,该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序逐个执行,直至微任务队列清空。也就是微任务优先级比宏任务高,且与微任务所处的代码位置无关。至此一个事件循环结束。如此循环的重复过程称为 Event Loop,每次的循环操作被称为 tick。

首次执行时,解释器会将整体代码放入宏任务队列中,因此事件循环是从第一个宏任务开始的。

在执行微任务过程中所产生的微任务要添加到微任务队列,也需要一起清空;微任务队列未清空前,不会执行下一个宏任务。

/* Test01 => 成功了1 成功了2 timeout1 timeout2 */
setTimeout(() => { console.log("timeout1"); });
setTimeout(() => { console.log("timeout2"); });
Promise.resolve("z").then((value) => { console.log("成功了1"); });
Promise.resolve("s").then((value) => { console.log("成功了2"); });
/* Test02 => 成功了3 成功了4 timeout2 timeout1 成功了5 timeout3 */
setTimeout(() => {
  console.log("timeout1");
  setTimeout(() => {
    console.log("timeout3");
  });
  Promise.resolve(5).then((value) => {
    console.log("成功了5");
  });
}, 5000);
setTimeout(() => {
  console.log("timeout2");
}, 3000);
Promise.resolve(3).then((value) => {
  console.log("成功了3");
});
Promise.resolve(4).then((value) => {
  console.log("成功了4");
});
/* Test03 => 1 7 2 3 8 4 6 5 0 */
setTimeout(() => {
  console.log("0");
});
new Promise((resolve, reject) => {
  console.log("1");
  resolve();
})
  .then(() => {
    console.log("2");
    new Promise((resolve, reject) => {
      console.log("3");
      resolve();
    })
      .then(() => {
        console.log("4");
      })
      .then(() => {
        console.log("5");
      });
  })
  .then(() => {
    console.log("6");
  });
new Promise((resolve, reject) => {
  console.log("7");
  resolve();
}).then(() => {
  console.log("8");
});
async function async1() {
  console.log('async1 start');
  await async2();
  console.log('async1 end');
}
async function async2() {
  console.log('async2');
}
 
console.log('script start');
setTimeout(function() {
  console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
  console.log('promise1');
  resolve();
}).then(function() {
  console.log('promise2');
});
console.log('script end');
// script start - async1 start - async2 - promise1 - script end - async1 end - promise2 - setTimeout

async await 配合时 await 后代码的执行时机 => 因 async 函数总是返回一个期约实例,那么 await 可看作 excutor 在执行 resolve 后 then 执行 onResolved 的结果。

async function async1(){
  await async2()
  console.log('async1 end')
}
// 等价于
async function async1() {
  return new Promise(resolve => {
    resolve(async2())
  }).then(() => {
    console.log('async1 end')
  })
}

Promise 构造器里的 resolve(async2()) 并不等于 Promise.resolve(async2()),因为 async2() 返回期约实例,是一个 thenable 对象。

thenable 对象即带有 then 方法

Promise 构造函数中的 resolve(thenable) 不等价于 Promise.resolve(thenable);Promise 构造函数中的 resolve(non-thenable) 等价于 Promise.resolve(non-thenable)。SOF

new Promise(resolve=>{
  resolve(thenable)
})
// 等价于
new Promise(resolve => {
  Promise.resolve().then(() => {
    thenable.then(resolve)
  })
})
// 最终转换结果
async function async1() {
  return new Promise(resolve => {
    Promise.resolve().then(() => {
      async2().then(resolve)
    })
  }).then(() => {
    console.log('async1 end')
  })
}

setImmediate is actually a special timer that runs in a separate phase of the event loop; setTimeout is executed by calculating a delay time;

process.nextTick is not technically part of the event loop. The nextTickQueue will be processed after the current operation is completed, regardless of the current phase of the event loop.

class Lib extends require('events').EventEmitter {
  constructor () {
    super()
    this.emit('init')
  }
}
const lib = new Lib();
lib.on('init', _ => { console.log('init!'); });  // 不执行
class Lib extends require('events').EventEmitter {
  constructor () {
    super()
    process.nextTick(_ => {
      this.emit('init')
    })
  }
}
const lib = new Lib();
lib.on('init', _ => { console.log('init!'); });  // 执行

中止 Promise

Promise 的缺点在于创建后无法取消,所以本质上 Promise 是无法被终止的。

  • 返回 pending 的 promise,使后续 then、catch、finally 不会继续执行
  • 抛出特殊的错误对象,然后在调用链后面的 catch 回调里进行后续操作
  • 封装传入待中止的 promise 的函数,与封装函数内部定时的 promise 竞速

BUGS 处理

  • promise.html:112 Uncaught (in promise) failed

当前存在失败的 Promise,且没有对应回调对结果进行处理。处理方式是在报错位置的 Promise 后以 catch 去处理。

  • new Promise("ok") => Uncaught TypeError: Promise resolver ok is not a function

传入必须是一个 excutor,传入非 excutor 返回 Promise 考虑 Promise.resolve(??)

let okp = Promise.resolve("ok")
console.log(okp)
/*
Promise («fulfilled>: 'ok'}
  [[Prototype]]: Promise
  [[PromiseState]]: "fulfilled"
  [[PromiseResult]]: "ok"
*/

结束

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处!