day-068-sixty-eight-20230512-Promise进阶-项目环境的问题-async与await
Promise进阶
- 创建Promise实例对象的方案
- 方案一:new Promise()构造函数,都会返回一个全新的Promise实例对象。
- 用new执行。
- 返回一个Promise实例对象。
- 方案二:每一次执行
旧Promise实例对象.then()
方法,都会返回一个全新的新Promise实例对象
。let 新Promise实例对象 = 旧Promise实例对象.then(onfulfilled,onrejected)
;- 返回一个全新的
新Promise实例对象
。新Promise实例对象
的状态和值,和旧Promise实例对象
没有关系,和onfulfilled与onrejected这两个回调函数中的任意一个方法
执行的细节有关。- 首先看方法执行是否报错,如果报错了,则
新Promise实例对象
是失败状态,值是报错原因。 - 如果没有报错,再看方法执行的返回值,别名
返回值X
。- 如果返回的是一个
新的返回Promise实例对象
。-
如果
返回值X
与旧Promise实例对象
是同一个对象,则新Promise实例对象
的状态是失败的,值就是抛出的异常。let p1 = new Promise(function(resolve){resolve(222)}).then() let p2 = p1.then(()=>{return p1})//caught SyntaxError: Identifier 'p1' has already been declared
-
如果
返回值X
是一个标准的Promise官方实例对象,则新Promise实例对象
的状态和值就是返回值X
的状态和值。 -
还需要判断
返回值X
是否是一个对象或者函数,因为它可能是一个伪Promise实例对象。 -
如果是伪Promise实例。
- 访问伪Promise实例。
- 如果报错了,则让
新Promise实例对象
的状态是失败状态,值就是报错原因。 - 这个是因为使用了类似于defineProperty方法对一个对象做了get劫持。
- 如果不报错。
- 如果then是一个函数,把then方法执行。
- 函数中的this需要是
返回值X
。 - then.call(x)或者x.then()。
- 那么传递给该then方法传递onfulfilled/onrejected;
- …
- 如果then不是函数,则说明
返回值X
不是Promise实例。那么新Promise实例对象
的状态是成功状态,值就是返回值。
- 如果报错了,则让
- 访问伪Promise实例。
-
- 具体可看Promises的A+规范。
- 如果不是新的实例,则新Promise实例对象是成功状态,值就是返回值。
- 如果返回的是一个
- 首先看方法执行是否报错,如果报错了,则
- 这个就是可以让then可以链式运行。
- Promise实例对象玩的核心主要就是then()链。
- Promise.reject()这一类,快捷创建Promise实例。
- Promise.all()这一类,创建计算后得到的的新相关Promise实例。
- 把函数用async修饰,返回值就是一个Promise实例对象。
- 方案一:new Promise()构造函数,都会返回一个全新的Promise实例对象。
- then的穿透顺延机制
-
每一次执行then方法的时候,如果需要传递的onfulfilled与inrejected并没有传递,则会把上一个实例的状态和值,顺延给下一个实例,就是执行then返回的这个实例。
let p1 = Promise.resolve(100); let p2 = p1.then(); p2.then( (value) => { console.log(`value-->`, value); }, (reason) => { console.log(`reason-->`, reason); } );
-
原理:
let p1 = Promise.reject(100); let p2 = p1.then( (value) => { return value; }, (reason) => { throw reason; } ); p2.then( (value) => { console.log(`value-->`, value); }, (reason) => { console.log(`reason-->`, reason); } );
- 给then执行一些默认方法。
-
-
平时写代码,then一般只传递onfulfilled,最后基于catch实现失败的处理。
- 而且不论上述那个环节出现失败,都会走到最后的catch中。
- 如果出现失败的Promise实例对象,但是没有对失败进行处理,则控制台会抛出红色警告,此警告不影响后续代码执行。
- 而且不论上述那个环节出现失败,都会走到最后的catch中。
-
- Promise.all()与Promise.any()这一类代码。
- 创建一个总的Promise实例对象,别名P
- 总实例的状态和值,取决于包含多个promise实例对象的集合promises中每个实例的状态和值。
- all()方法:每个实例的状态都为成功,总实例的状态才为成功,值是一个数组,按照顺序存储了每个实例成功的值。
- 其中只要有一个实例的状态是失败,则总实例就是失败,值就是此失败实例的值,后续的实例都不处理了!
- any()方法:只要有一个成功,整体就是成功,值也是成功这一项的值,后续的也不管了。
- 只有所有实例都失败,总实例才为失败,值是一句证明都失败的话。
- race方法:那个实例先处理完毕,总实例的状态和值就按照它的来。
- all()方法:每个实例的状态都为成功,总实例的状态才为成功,值是一个数组,按照顺序存储了每个实例成功的值。
- Promise集合中的每一项,都必须是Promise实例,如果出现一项不是,则默认会把其转换为Promise实例
- 状态是成功的,值是本身。
- 要求promises这个集合,需要是符合迭代器规范的集合
Symbol.iterator
。
手写的源码
(() => {
"use strict"; //使用严格模式,让JavaScript的自由度缩小,进而让代码更规范。
// 思路来源于: [promise A+规范 英文官网](https://promisesaplus.com/)
// 为对象设置不可枚举的属性;
var define = function define(obj, key, value) {
Object.defineProperty(obj, key, {
writable: true,
configurable: true,
enumerable: false,
value: value,
});
};
// 确保是Promise类的实例
var ensureInstance = function ensureInstance(obj) {
if (!(obj instanceof Promise)) {
// 必须要在Promise实例上被调用-不能被call或bind改为其它的对象。
throw new TypeError(
"Method Promise.prototype.then called on incompatible receiver " + obj
);
}
};
// 验证是否为Promise实例对象-包括伪Promise实例对象。
var isPromise = function isPromise(x) {
if (x !== null && /^(object|function)$/.test(typeof x)) {
var then;
try {
then = x.then;
} catch (err) {
// console.log(`err-->`, err);
return false;
}
// 返回值x是一个promise实例-伪实例或者是thenable
if (typeof then === "function") {
return true;
}
}
return false;
};
// 基于定时器模拟queueMicrotask创建异步微任务-不过这里创建的是异步宏任务
var queueTask = function queueTask(callback) {
// 原则上还需要判断callback是否是个函数,不过这个方法只在内部使用,不用做太多检测。
if (typeof queueMicrotask !== "undefined") {
queueMicrotask(callback);
return;
}
var timer = setTimeout(function () {
callback();
clearTimeout(timer);
}, 0);
};
// 构造函数
var Promise = function Promise(executor) {
// console.log(new.target)
// console.log("this-->", this);
var self = this;
// 得用new执行
if (!(self instanceof Promise)) {
throw new TypeError(
"Promise constructor cannot be invoked without 'new'"
);
return;
}
// 得传入一个函数
if (typeof executor !== "function") {
throw new TypeError("Promise resolver undefined is not a function");
return;
}
// 为实例设置私有属性:状态和值
// self.state = "pending";
// self.result = undefined;
define(self, "state", "pending");
define(self, "result", undefined);
// 为实例设置私有属性:成功回调函数集合、失败回调函数集合。
define(self, "onfulfilledCallback", []);
define(self, "onrejectedCallback", []);
// console.log(`self-->`, self);
// 修改实例状态和值
var change = function change(state, result) {
// 状态一旦被更改后,则后续就不能再被更改了。
if (self.state !== "pending") {
return;
}
self.state = state;
self.result = result;
// 通知集合中的方法执行-通过异步微任务的方式执行
// self.then(self.onfulfilled, self.onrejected);
queueTask(function () {
var callbacks =
self.state === "fulfilled"
? self.onfulfilledCallback
: self.onrejectedCallback;
callbacks.forEach(function (callback) {
callback(self.result);
});
});
};
try {
// 立即执行executor函数
executor(
function resolve(value) {
change("fulfilled", value);
},
function reject(reason) {
change("rejected", reason);
}
);
} catch (err) {
// 如果executor函数执行报错,也会把实例的状态改为失败,值就是报错原因。
change("rejected", err);
}
};
// 原型对象
var proto = Promise.prototype;
// promise = "要返回的promise实例对象",
// x = "返回值",
// resolve = "返回值",
// reject = "返回值",
var Resolve = function Resolve(promise, x, resolve, reject) {
// 根据返回值x,来决定新返回的实例promise的状态和值。
if (x === promise) {
throw new TypeError("新返回的promise实例对象不能循环");
}
if (x !== null && /^(object|function)$/.test(typeof x)) {
var then;
try {
then = x.then;
} catch (err) {
reject(err);
return;
}
// 返回值x是一个promise实例-伪实例或者是thenable
if (typeof then === "function") {
var called = false; //有没有被执行。
try {
then.call(
x,
function resolvePromise(y) {
if (called) {
return;
}
called = true;
// resolve(y);//不能直接这样写,因为x成功后的值y,还可能是promise实例。如果y是一个新实例,则他决定了最终的结果 -- 递归处理y。
Resolve(promise, y, resolve, reject);
},
function rejectPromise(r) {
if (called) {
return;
}
called = true;
reject(r);
}
);
} catch (err) {
if (called) {
return;
}
called = true;
reject(err);
}
return;
}
}
// 返回值绝对不是一个promise实例,则让then返回的新实例:状态是成功,值就是返回值x;
resolve(x);
};
// 设置then()方法。
define(proto, "then", function then(onfulfilled, onrejected) {
var self = this;
ensureInstance(self);
if (typeof onfulfilled !== "function") {
onfulfilled = function (value) {
return value;
};
}
if (typeof onrejected !== "function") {
onrejected = function (reason) {
throw reason;
};
}
var promise = new Promise(function (resolve, reject) {
switch (self.state) {
case "fulfilled":
queueTask(function () {
try {
var x = onfulfilled(self.result);
Resolve(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
break;
case "rejected":
queueTask(function () {
try {
var x = onrejected(self.result);
Resolve(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
break;
default:
// self.onfulfilled=onfulfilled
// self.onrejected=onrejected
self.onfulfilledCallback.push(function (result) {
// 传递的是匿名函数,但匿名函数可以通过作用域链拿到promise, resolve, reject。
// 不放进匿名函数中,则只能拿到x。
try {
var x = onfulfilled(result);
Resolve(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
self.onrejectedCallback.push(function (result) {
try {
var x = onrejected(result);
Resolve(promise, x, resolve, reject);
} catch (err) {
reject(err);
}
});
break;
}
});
return promise;
});
define(proto, "catch", function (onfulfilled) {
var self = this;
ensureInstance(self);
return self.then(null, onfulfilled);
});
// 静态私有属性方法
define(Promise, "resolve", function resolve(x) {
var promise = new Promise(function (resolve, reject) {
Resolve(promise, x, resolve, reject);
});
return promise;
});
define(Promise, "reject", function reject(x) {
return new Promise(function (_, reject) {
reject(x);
});
});
define(Promise, "all", function all(promises) {
return new Promise(function (resolve, reject) {
// 入参不是可迭代对象
if (!Array.isArray(promises)) {
throw new TypeError("promises is not array");
}
var num = 0;
var result = [];
// 如果集合中一项实例都没有,则整体也是成功,值是空数组。
if (promises.length === 0) {
resolve([]);
return;
}
// 迭代集合中的每一项,观察每一项的状态和值---每一轮都产生了一个闭包
promises.forEach(function (item, index) {
// 必须保证每一项都是Promise实例对象。
if (!isPromise(item)) {
item = Promise.resolve(item);
}
item
.then(function (value) {
// 有一项为成功,就记录一项。
num++;
result[index] = value; //索引要记录为入参的对应索引。
// 都成功了,现是整体返回的实例就是成功。
if (num >= promises.length) {
resolve(result);
}
})
.catch(function (reason) {
// 有一项失败,则整体返回的Promise实例对象的状态就是失败。
reject(reason);
});
});
// for(var i=0;i<promises.length;i++){
// (function(i){
// })(i)
// }
});
});
define(Promise, "any", function any(promises) {
return new Promise(function (resolve, reject) {
// 入参不是可迭代对象
if (!Array.isArray(promises)) {
throw new TypeError("promises is not array");
}
if (promises.length === 0) {
reject(new Error("AggregateError: All promises were rejected"));
return;
}
var num = 0;
promises.forEach(function (item, index) {
if (!isPromise(item)) {
item = Promise.resolve(item);
}
item
.then(function (value) {
// 有一项为成功了,现是整体返回的实例就是成功。
resolve(result);
})
.catch(function (reason) {
// 有一项为失败,就记录一项。
num++;
if (num >= promises.length) {
reject("都失败了");
}
});
});
// for(var i=0;i<promises.length;i++){
// (function(i){
// })(i)
// }
});
});
// 加测试-插件要求-插件为promises-aplus-tests。
Promise.deferred = function deferred() {
var result = {};
result.promise = new Promise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
};
// Promise.resolve(100)
// Promise.resolve(Promise.reject(-1))
// Promise.reject(0)
// Promise.reject(Promise.resolve(100))
// 暴露API-让其支持各种环境
if (typeof window !== "undefined") {
window.Promise = Promise;
}
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = Promise;
}
})();
一些好玩项目
- 新风商城后台管理系统
- 账号密码:admin 123456
- 账号:admin
- 密码:123456
- 账号密码:admin 123456
- 新风商城APP端
- 物美后台管理系统
- 网易云音乐后台
项目环境的问题
- 跑环境也就是按照 package.json 中的开发依赖和生产依赖,把需要的模块都安装一遍。
- 一般就是项目所需的各种第三方插件,如jQuery、vue这类的。
- 也有一些是开发工具。
- 如scss等预编译工具。
- 有根目录中看到对应的lock文件后,就用对应的包管理工具。
/package.lock
用npm。- npm install
/yarn.lock
用yarn。- yarn
/pnpm-lock.yaml
用pnpm。- pnpm install
- 运行脚本命令,一般独立于环境安装。即可以跑环境用yarn,运行命令使用npm。
- pnpm
- pnpm
- yarn
- yarn 脚本命令
- npm
- npm run 脚本命令
- pnpm
- 要学会看英文文档。
- 看英文文档时。
- 最好是先看一下全文中文翻译。
- 之后看英文文档配合翻译工具来做。
- 一般是一段英文一段地来看。
- 然后是看英文文档带猜配合翻译工具。
- 一般是单独看单个单词,其它的基本上都理解了。
- 最后是看英文文档自己理解。
- 原因:
- 看特定行业,一般得用英文文档,中文文档一般是二手的,或者没充分测试,可能有坑。
- 前端新规范:
- 规范之前是草案,先看草案去猜。
- 规范之后是有官方接口,同时也有官方文档。
- 尝试用英文文档去看,之后用。
- 1到3年看一遍ES6官方离线文档。
- ES6官方文档。
- 当前目录有离线版本的。
- MDN文档。
- w3c英文官网。
- 掘金小册。
- stackoverflow。
- github。
- ES6官方文档。
- 看英文文档时。
async与await
- async与await:简化promise操作的语法糖 promise+generator的语法糖
- async的作用:async是用来修饰函数的。
-
让函数的返回值变为一个Promise实例对象。
- 方法执行中遇到await,会等待await执行的处理结果。
- 这一步是异步微任务的,会执行其它同步代码。
- 方法执行报错,则返回失败的Promise实例对象,值是报错原因。
- 方法执行不报错,方法执行结束时看方法执行的返回值。
- 如果不是一个新的实例,则返回的实例是成功,值是返回值。
- 如果返回的是一个新的实例,则返回的实例和这个新实例保持同步。
- 方法执行中遇到await,会等待await执行的处理结果。
-
想要在函数中使用await,则当前函数必须经过async的修饰。
let result = await Promise实例对象
- await后面要跟一个Promise实例对象。
await fn()
先执行fn函数,执行的返回值作为await等待的操作。- 如果不放,它会自动把后面的值处理为成功状态的Promise实例对象的返回值。
await 10
- 如果后面的实例是成功状态。
- result存储的就是成功的结果。
- 当前上下文中,下面的代码可以继续执行。
- 如果后面的实例是失败状态,则下面的代码就不会执行了。
- 下面代码不会执行,并且抛出一个不影响后续操作的错误。
- await后面要跟一个Promise实例对象。
-
- await会等待一个Promise实例对象的处理结果
- 当await后面的实例是失败的,而且没有对失败进行处理,会抛出异常,这样就导致fn执行返回的实例也是失败的状态,值就是抛出的异常。
- await也是一个异步的微任务
- 它仅仅是在函数中,模拟出了同步的效果,它本身是异步的。
- 只要遇到await,await后面的代码都会变成一个微任务,等待await后面的实例为成功时,才会把它放到EventQueue中的微任务队列中。
- 此时async函数的同步代码就相当于暂时执行完了,先执行async函数后面的同步代码。
- async的作用:async是用来修饰函数的。
进阶参考
- [ES6官方离线文档](./ES6官方离线文档----ECMAScript 2015 Language Specification – ECMA-262 6th Edition.mhtml)
- stackoverflow
- .msi和.exe 文件的区别
- msi和exe安装文件有什么区别