[实践系列]Promises/A+规范

前言

[实践系列] 主要是让我们通过实践去加深对一些原理的理解。

实践系列-前端路由

实践系列-Babel原理

有兴趣的同学可以关注 实践系列 。 求star求follow~

什么是Promise ?

Promise是JS异步编程中的重要概念,异步抽象处理对象,是目前比较流行Javascript异步编程解决方案之一

Promises/A+ 规范

为实现者提供一个健全的、可互操作的 JavaScript promise 的开放标准。

术语

  • 解决 (fulfill) : 指一个 promise 成功时进行的一系列操作,如状态的改变、回调的执行。虽然规范中用 fulfill 来表示解决,但在后世的 promise 实现多以 resolve 来指代之。
  • 拒绝(reject) : 指一个 promise 失败时进行的一系列操作。
  • 拒因 (reason) : 也就是拒绝原因,指在 promise 被拒绝时传递给拒绝回调的值。
  • 终值(eventual value) : 所谓终值,指的是 promise 被解决时传递给解决回调的值,由于 promise 有一次性的特征,因此当这个值被传递时,标志着 promise 等待态的结束,故称之终值,有时也直接简称为值(value)。
  • Promise : promise 是一个拥有 then 方法的对象或函数,其行为符合本规范。
  • thenable : 是一个定义了 then 方法的对象或函数,文中译作“拥有 then 方法”。
  • 异常(exception) : 是使用 throw 语句抛出的一个值。

基本要求

下面我们先来讲述Promise/A+ 规范的几个基本要求。

1. Promise的状态

一个Promise的当前状态必须是以下三种状态中的一种: 等待状态(Pending) 执行状态(Fulfilled)拒绝状态(Rejected)。


const PENDING = 'pending';const FULFILLED = 'fulfilled';const REJECTED = 'rejected';

等待状态 (Pending)

处于等待态时,promise 需满足以下条件:

  • 可以迁移至执行态或拒绝态
 if (this.state === PENDING) {this.state = FULFILLED || REJECTED ;}

执行状态 (Fulfilled)

处于执行态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的终值
 this.value = value;

拒绝状态 (Rejected)

处于拒绝态时,promise 需满足以下条件:

  • 不能迁移至其他任何状态
  • 必须拥有一个不可变的据因
 this.reason = reason;

这里的不可变指的是恒等(即可用 === 判断相等),而不是意味着更深层次的不可变(译者注:盖指当 value 或 reason 不是基本值时,只要求其引用地址相等,但属性值可被更改)

2. Then 方法

一个 promise 必须提供一个 then 方法以访问其当前值、终值和据因。

promise 的 then 方法接受两个参数:

promise.then(onFulfilled, onRejected)

参数可选

onFulfilled 和 onRejected 都是可选参数。

  • 如果 onFulfilled 不是函数,其必须被忽略
  • 如果 onRejected 不是函数,其必须被忽略

onFulfilled 特性

如果 onFulfilled 是函数:

  • 当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值
  • 在 promise 执行结束前其不可被调用
  • 其调用次数不可超过一次

onRejected 特性

如果 onRejected 是函数:

  • 当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因
  • 在 promise 被拒绝执行前其不可被调用
  • 其调用次数不可超过一次

调用时机

onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用 注1

注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。

这个事件队列可以采用“宏任务(macro - task)”机制或者“微任务(micro - task)”机制来实现。

由于 promise 的实施代码本身就是平台代码(译者注:即都是 JavaScript),故代码自身在处理在处理程序时可能已经包含一个任务调度队列。

调用要求

onFulfilled 和 onRejected 必须被作为函数调用(即没有 this 值)

多次调用

then 方法可以被同一个 promise 调用多次

  • 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
  • 当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调

简易版实践

我们先通过实践一个简易版的Promise来消化一下上面Promises/A+规范的基本要求。

首先

npm init // 测试实现是否符合 promises/A+ 规范npm install promises-aplus-tests -D 

package.json

{"name": "ajpromise","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "promises-aplus-tests ./simple.js"},"author": "webfansplz","license": "MIT","devDependencies": {"promises-aplus-tests": "^2.1.2"}
}

simple.js

//Promise 的三种状态  (满足要求 -> Promise的状态)
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';class AjPromise {constructor(fn) {//当前状态this.state = PENDING;//终值this.value = null;//拒因this.reason = null;//成功态回调队列this.onFulfilledCallbacks = [];//拒绝态回调队列this.onRejectedCallbacks = [];//成功态回调const resolve = value => {// 使用macro-task机制(setTimeout),确保onFulfilled异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。setTimeout(() => {if (this.state === PENDING) {// pending(等待态)迁移至 fulfilled(执行态),保证调用次数不超过一次。this.state = FULFILLED;// 终值this.value = value;this.onFulfilledCallbacks.map(cb => {this.value = cb(this.value);});}});};//拒绝态回调const reject = reason => {// 使用macro-task机制(setTimeout),确保onRejected异步执行,且在 then 方法被调用的那一轮事件循环之后的新执行栈中执行。 (满足要求 -> 调用时机)setTimeout(() => {if (this.state === PENDING) {// pending(等待态)迁移至 fulfilled(拒绝态),保证调用次数不超过一次。this.state = REJECTED;//拒因this.reason = reason;this.onRejectedCallbacks.map(cb => {this.reason = cb(this.reason);});}});};try {//执行promisefn(resolve, reject);} catch (e) {reject(e);}}then(onFulfilled, onRejected) {typeof onFulfilled === 'function' && this.onFulfilledCallbacks.push(onFulfilled);typeof onRejected === 'function' && this.onRejectedCallbacks.push(onRejected);// 返回this支持then 方法可以被同一个 promise 调用多次return this;}
}

就这样,一个简单的promise就完成了.

new AjPromise((resolve, reject) => {setTimeout(() => {resolve(2);}, 2000);
}).then(res => {console.log(res);return res + 1;}).then(res => {console.log(res);});//output  // delay 2s..
//  2 
//  3 

接下来,我们来看看我们的实现是否完全符合promises/A+规范~

npm run test

GG,测试用例只过了一小部分,大部分飘红~

OK,接下来,我们来继续了解promises/A+ 进一步的规范要求~

进一步要求

由于接下来的要求比较抽象和难理解,所以我们将一步一步实践来加深理解。

1. 返回

  • 1.then方法必须返回一个promise对象
  • 2.如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)
  • 3.如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。
  • 4.如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值。
  • 5.如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。
  • 6.不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。

我们通过以上要求来一步一步完善then方法
1.

// 1.首先,then方法必须返回一个promise对象then(onFulfilled, onRejected) {let newPromise;return (newPromise = new AjPromise((resolve, reject) => {}));}

2.

  then(onFulfilled, onRejected) {let newPromise;return (newPromise = new AjPromise((resolve, reject) => {// 2.如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行下面的 Promise 解决过程:[[Resolve]](promise2, x)this.onFulfilledCallbacks.push(value => {let x = onFulfilled(value);//解决过程 resolvePromiseresolvePromise(newPromise, x);});this.onRejectedCallbacks.push(reason => {let x = onRejected(reason);//解决过程 resolvePromiseresolvePromise(newPromise, x);});}));}// 解决过程function resolvePromise() {//...}

3.

  then(onFulfilled, onRejected) {let newPromise;return (newPromise = new AjPromise((resolve, reject) => {//  3.如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e。this.onFulfilledCallbacks.push(value => {try {let x = onFulfilled(value);resolvePromise(newPromise, x);} catch (e) {reject(e);}});this.onRejectedCallbacks.push(reason => {try {let x = onRejected(reason);resolvePromise(newPromise, x);} catch (e) {reject(e);}});}));}

4,5.

  then(onFulfilled, onRejected) {  let newPromise;// 4.如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值。onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;// 5.如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因。onRejected =typeof onRejected === 'function'? onRejected: reason => {throw reason;};return (newPromise = new AjPromise((resolve, reject) => {this.onFulfilledCallbacks.push(value => {try {let x = onFulfilled(value);resolvePromise(newPromise, x);} catch (e) {reject(e);}});this.onRejectedCallbacks.push(reason => {try {let x = onRejected(reason);resolvePromise(newPromise, x);} catch (e) {reject(e);}});}));}

6.

  then(onFulfilled, onRejected) {let newPromise;onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;onRejected =typeof onRejected === 'function'? onRejected: reason => {throw reason;};// 2.2.6规范 对于一个promise,它的then方法可以调用多次.// 当在其他程序中多次调用同一个promise的then时 由于之前状态已经为FULFILLED / REJECTED状态,则会走以下逻辑,// 所以要确保为FULFILLED / REJECTED状态后 也要异步执行onFulfilled / onRejected ,这里使用setTimeout// 6.不论 promise1 被 reject 还是被 resolve 时 promise2 都会被 resolve,只有出现异常时才会被 rejected。// 由于在接下来的解决过程中需要调用resolve,reject进行处理,处理我们在调用处理过程时,传入参数if (this.state == FULFILLED) {  return (newPromise = new AjPromise((resolve, reject) => {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}if (this.state == REJECTED) {return (newPromise = new AjPromise((resolve, reject) => {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}if (this.state === PENDING) {return (newPromise = new AjPromise((resolve, reject) => {this.onFulfilledCallbacks.push(value => {try {let x = onFulfilled(value);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});this.onRejectedCallbacks.push(reason => {try {let x = onRejected(reason);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}}

ok,完整的then方法搞定了。相信通过以上实践,你对返回要求已经有了更深的理解。

2. Promise 解决过程

Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。

这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。

运行 [[Resolve]](promise, x) 需遵循以下步骤:

1。x 与 promise 相等

如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise。

2。x 为 Promise

  • 如果 x 为 Promise ,则使 promise 接受 x 的状态。
  • 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝。
  • 如果 x 处于执行态,用相同的值执行 promise。
  • 如果 x 处于拒绝态,用相同的据因拒绝 promise。

3。x 为对象或函数

如果 x 为对象或者函数:

  • 把 x.then 赋值给 then。
  • 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise。
  • 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:

    • 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
    • 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
    • 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
    • 如果调用 then 方法抛出了异常 e:

      • 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
      • 否则以 e 为据因拒绝 promise
    • 如果 then 不是函数,以 x 为参数执行 promise
  • 如果 x 不为对象或者函数,以 x 为参数执行 promise

如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise 。

1.x 与 promise 相等

function resolvePromise(promise2, x, resolve, reject) {//x 与 promise 相等 //如果从onFulfilled中返回的x 就是promise2 就会导致循环引用报错//如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promiseif (x === promise2) {reject(new TypeError('循环引用'));}
}

2.x 为 Promise。

function resolvePromise(promise2, x, resolve, reject) {if (x === promise2) {reject(new TypeError('循环引用'));}// x 为 Promiseelse if (x instanceof AjPromise) {// 如果 x 为 Promise ,则使 promise 接受 x 的状态// 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝if (x.state === PENDING) {x.then(y => {resolvePromise(promise2, y, resolve, reject);},reason => {reject(reason);});} else {// 如果 x 处于执行态,用相同的值执行 promise// 如果 x 处于拒绝态,用相同的据因拒绝 promisex.then(resolve, reject);}}
}

3.x 为对象或函数

function resolvePromise(promise2, x, resolve, reject) {if (x === promise2) {reject(new TypeError('循环引用'));}if (x instanceof AjPromise) {if (x.state === PENDING) {x.then(y => {resolvePromise(promise2, y, resolve, reject);},reason => {reject(reason);});} else {x.then(resolve, reject);}} else if (x && (typeof x === 'function' || typeof x === 'object')) {// 避免多次调用let called = false;try {//把 x.then 赋值给 thenlet then = x.then;if (typeof then === 'function') {// 如果 then 是函数,将 x 作为函数的作用域 this 调用之。// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用then.call(x,// 如果 resolvePromise 以值 y 为参数被调用,则运行[[Resolve]](promise, y)y => {if (called) return;called = true;resolvePromise(promise2, y, resolve, reject);},// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promiser => {if (called) return;called = true;reject(r);});}else {// 如果 then 不是函数,以 x 为参数执行 promiseresolve(x);}  } catch (e) {// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise// 如果调用 then 方法抛出了异常 e:// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之// 否则以 e 为据因拒绝 promiseif (called) return;called = true;reject(e);}} else {// 如果 x 不为对象或者函数,以 x 为参数执行 promiseresolve(x);}
}

Ok~比较复杂的解决过程也让我们搞定了.接下来我们整合下代码

Promises/A+ 规范 实践


const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';class AjPromise {constructor(fn) {this.state = PENDING;this.value = null;this.reason = null;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];const resolve = value => {if (value instanceof Promise) {return value.then(resolve, reject);}setTimeout(() => {if (this.state === PENDING) {this.state = FULFILLED;this.value = value;this.onFulfilledCallbacks.map(cb => {cb = cb(this.value);});}});};const reject = reason => {setTimeout(() => {if (this.state === PENDING) {this.state = REJECTED;this.reason = reason;this.onRejectedCallbacks.map(cb => {cb = cb(this.reason);});}});};try {fn(resolve, reject);} catch (e) {reject(e);}}then(onFulfilled, onRejected) {let newPromise;onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;onRejected =typeof onRejected === 'function'? onRejected: reason => {throw reason;};if (this.state === FULFILLED) {return (newPromise = new AjPromise((resolve, reject) => {setTimeout(() => {try {let x = onFulfilled(this.value);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}if (this.state === REJECTED) {return (newPromise = new AjPromise((resolve, reject) => {setTimeout(() => {try {let x = onRejected(this.reason);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}if (this.state === PENDING) {return (newPromise = new AjPromise((resolve, reject) => {this.onFulfilledCallbacks.push(value => {try {let x = onFulfilled(value);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});this.onRejectedCallbacks.push(reason => {try {let x = onRejected(reason);resolvePromise(newPromise, x, resolve, reject);} catch (e) {reject(e);}});}));}}
}
function resolvePromise(promise2, x, resolve, reject) {if (x === promise2) {reject(new TypeError('循环引用'));}if (x instanceof AjPromise) {if (x.state === PENDING) {x.then(y => {resolvePromise(promise2, y, resolve, reject);},reason => {reject(reason);});} else {x.then(resolve, reject);}} else if (x && (typeof x === 'function' || typeof x === 'object')) {let called = false;try {let then = x.then;if (typeof then === 'function') {then.call(x,y => {if (called) return;called = true;resolvePromise(promise2, y, resolve, reject);},r => {if (called) return;called = true;reject(r);});} else {resolve(x);}} catch (e) {if (called) return;called = true;reject(e);}} else {resolve(x);}
}AjPromise.deferred = function() {let defer = {};defer.promise = new AjPromise((resolve, reject) => {defer.resolve = resolve;defer.reject = reject;});return defer;
};module.exports = AjPromise;

再来看看我们的实现是否符合Promises/A+规范

npm run test

nice,测试用例全部通过!

源码地址

传送门

如果觉得有帮助到你,请给个star支持下作者~

参考文献

Promises/A+规范译文

Promise详解与实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/276131.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web Components 上手指南

现在的前端开发基本离不开 React、Vue 这两个框架的支撑,而这两个框架下面又衍生出了许多的自定义组件库:Element(Vue)Ant Design(React)这些组件库的出现,让我们可以直接使用已经封装好的组件&…

如何远程连接Windows和linux服务器

linux的方法在下面 Windows服务器远程连接 登录控制台查看服务器系统是什么系统例如阿里云的ECS服务器 Windows系统可以使用微软自带的远程工具进行连接,可以连接的系统有Windows server 和Windows 7-10 等等系列;Windows系统,例如Windows10系…

手把手教你接入前端热门抓包神器 - whistle

大家好,我是若川,今天推荐腾讯前端团队的这篇好文。whistle 是一个基于 Node.js 的跨平台网络调试工具。最近随着 Nohost 的开源,有不少同学问了 whistle 相关的问题,本篇文章将结合几个常见的业务场景介绍如何在本地前端项目开发…

React 与 Vue 框架的设计思路大 PK

大家好,我是若川。今天分享一篇框架设计思路的好文。关于我 大家好我是花果山的大圣,今天很荣幸,有机会跟大家分享一下很多年轻人感兴趣的话题《 Vue 和 React 设计思想 PK》,个人水平有限,如果有理解不到位的请倾盆,大…

php foreach id是否存在数组_请纠正这 5 个 PHP 编码小陋习

在做过大量的代码审查后,我经常看到一些重复的错误,以下是纠正这些错误的方法。在循环之前测试数组是否为空$items [];// ...if (count($items) > 0) {foreach ($items as $item) {// process on $item ...}}foreach以及数组函数 (array_*) 可以处理…

1161转进制(C语言)

一:题目 二:思路分析 1.首先该题目让我们使用递归求十进制转其他进制 2.其次,我们要知道十进制转换为其他进制怎么转换,以例题所给的数据为例 由此图可以看出,十进制转换为其他进制,是辗转相除法&#xf…

应对无协议脱欧 葡萄牙机场将为英籍旅客设快速通道

中新网1月18日电 据台湾《联合报》援引英媒报道,英国首相特蕾莎•梅的脱欧协议遭下院否决后,英国无协议脱欧的可能性变大。葡萄牙总理科斯塔17日表示,里斯本当局正对机场开设特殊通道进行规划,使英国旅客无论英国最后如何脱欧&…

6轮字节前端校招面试经验分享

大家好,我是若川。最近金三银四,今天分享一篇字节前端校招面试经验的轻松好文,相信看完会有所收获。也欢迎点击下方卡片关注或者星标我的公众号若川视野因为我错过了2020年的秋招(ps: 那时候连数据结构与算法都还没学完&#xff0…

斥资近1亿港元,小米二次回购

1月21日消息,小米集团发布公告称,公司于1月18日回购了984.96万股B类普通股股票,占已发行股份0.041%,平均价为每股B类股10.1527港元,总计斥资近1亿港元。 这也是继1月17日首次回购后,小米集团连续两日出手进…

ios macos_设计师可以从iOS 14和macOS Big Sur中学到什么?

ios macos重点 (Top highlight)With the introduction of iOS 14 and macOS Big Sur, we are the witness of the next big thing in UI Design. Changes are not so revolutionary like in iOS 7 years before, but they undoubtedly present the trend UI Designers will fol…

网页设计简约_简约网页设计的主要功能

网页设计简约重点 (Top highlight)Minimalism is synonymous with simplicity. Not quite. As the name suggests, minimalism is definitely not about opulent design. But the assumption that minimalism is design-less and plain is also wrong. Minimalism is simple ye…

Expo 2010 Japan Pavilion

^_^转载于:https://www.cnblogs.com/mmmhhhlll/archive/2010/04/16/1713680.html

深度对比学习Vue和React两大框架

作为国内应用最广的两个框架,Vue 和 React 是前端必须掌握的内容,也是面试的重点。但大多数读者都只擅长其中一个框架,当面试涉及到另一个框架的内容时,就答不好了。比如虚拟dom,两个框架中都有应用,面试官…

java rwd_面向任务的设计-不仅限于Mobile First和RWD

java rwdWe already know that majority of solutions should start with a design for smartphones, we know that all websites should be responsive. Now, it’s time to think about holistic solutions with specific tasks adapted to all kind of devices.我们已经知道…

HOJ 1015 Nearly prime numbers

代码 //Nearly prime number is an integer positive number for which it is possible //to find such primes P1 and P2 that given number is equal to P1*P2.#include <stdio.h>#include <stdlib.h>#include <math.h>//decide n whither is a nearly pri…

「前端工程化」该怎么理解?

大家好&#xff0c;我是若川。今天分享一篇「前端工程化」的好文。非广告&#xff0c;请放心阅读。可点击下方卡片关注我&#xff0c;或者查看系列文章。今天发文比较晚&#xff0c;以往都是定时早上7:30发文&#xff0c;也不知道是不是有点早。一.什么是前端工程&#xff1f;一…

figma下载_Figma和ProtoPie中的原型制作,比较

figma下载第1部分 (Part 1) Prototyping has never had such a high profile with a whole host of tools that now give you varying ability to realize your designs beyond their static UI and into a working usable thing. It’s fair to say that prototyping within t…

「前端组件化」该怎么理解?

大家好&#xff0c;我是若川。今天分享一篇关于「前端组件化」的好文。欢迎点击下方卡片关注我。以下是正文~这里我们一起来学习前端组件化的知识&#xff0c;而组件化在前端架构里面是最重要的一个部分。讲到前端架构&#xff0c;其实前端架构中最热门的就有两个话题&#xff…

大屏设计的视觉统一_视觉设计中的统一

大屏设计的视觉统一视觉设计的统一性是什么&#xff1f; (What is unity in visual design?) The concept of unity in visual design means a group of elements working together to create a greater whole. It means, as the clich goes: A whole that is greater than th…

跟着官方文档能学懂React Hooks就怪了

大家好&#xff0c;我是若川。今天分享一篇关于「React Hooks」的好文。欢迎点击下方卡片关注我。以下是正文~回想下你入门Hooks的过程&#xff0c;是不是经历过&#xff1a;类比ClassComponent的生命周期&#xff0c;学习Hooks的执行时机慢慢熟练以后&#xff0c;发现Hooks的执…