资讯专栏INFORMATION COLUMN

性感的Promise,拥抱ta然后扒光ta

xushaojieaaa / 2526人阅读

摘要:,异步编程的流行解决方案,相比于古老的回调函数等方式,它更科学,更优雅。它来自民间,后被官方招安。实现方法,方法和状态机制根据使用方法我们可以知道,是一个需要接受一个执行器的构造函数,执行器提供两个方法,内部有状态机制,原型链上有方法。

Promise,js异步编程的流行解决方案,相比于古老的回调函数等方式,它更科学,更优雅。它来自民间,后被官方招安。 本文将从介绍用法开始,一步步了解Promise,探究源码,最终根据官方规范手写一个Promise。 让我们先拥抱ta,再扒光ta!

</>复制代码

  1. 我想在你身上,做春天在樱桃树身上做的事情。——巴勃罗·聂鲁达
1. How Promise?

创建Promise

首先来看看promise的用法,从名字可以看出它是个构造函数,所以我们得new它,得到一个Promise实例p,我们打印p看看

</>复制代码

  1. let p = new Promise
  2. console.log(p) // TypeError: Promise resolver undefined is not a function

参数

报错信息告诉我们,Promise需要一些参数,这里需要一个函数(我们叫它执行器)作为参数,该函数有两个参数————resolve和reject,这两个参数也是函数(由js引擎提供),我们可以在Promise内部调用,当异步操作成功时,调用resolve,否则reject。

</>复制代码

  1. let p =new Promise(function(resolve, reject){
  2. if(/* 异步操作成功 */){
  3. resolve(data)
  4. }else{
  5. reject(err)
  6. }
  7. })

state

现在我们需要知道一个重要概念,Promise是有“状态”的,分别是pending(等待态)、fulfilled(成功态)、rejected(失败态),pending可以转换为fulfilled或rejected,但fulfilled和rejected不可相互转化。

resolve/reject 方法

resolve方法可以将pending转为fulfilled,reject方法可以将pending转为rejected。

then方法

通过给Promise示例上的then方法传递两个函数作为参数,可以提供改变状态时的回调,第一个函数是成功的回调,第二个则是失败的回调。

</>复制代码

  1. p.then(function(data){ // resolve方法会将参数传进成功的回调
  2. console.log(data)
  3. }, function(err){ // reject方法会将失败的信息传进失败的回调
  4. console.log(err)
  5. })


举个栗子

</>复制代码

  1. let p = new Promise(function(resolve, reject){
  2. setTimeout(function(){
  3. let num = Math.random()
  4. if (num > 0.5) {
  5. resolve(num)
  6. }else{
  7. reject(num)
  8. }
  9. }, 1000)
  10. })
  11. p.then(function(num){
  12. console.log("大于0.5的数字:", num)
  13. },function(num){
  14. console.log("小于等于0.5的数字", num)
  15. })
  16. // 运行第一次:小于等于0.5的数字 0.166162996031475
  17. // 运行第二次:大于0.5的数字: 0.6591451548308984
  18. ...

在Promise执行器中我们进行了一次异步操作,并在我们觉得合适的时候调用成功或失败的回调函数,并拿到了想要的数据以进行下一步操作

链式调用

除此之外,每一个then方法都会返回一个新的Promise实例(不是原来那个),让then方法支持链式调用,并可以通过返回值将参数传递给下一个then

</>复制代码

  1. p.then(function(num){
  2. return num
  3. },function(num){
  4. return num
  5. }).then(function(num){
  6. console.log("大于0.5的数字:", num)
  7. },function(num){
  8. console.log("小于等于0.5的数字", num)
  9. })

catch方法

catch方法等同于.then(null, rejection),可以直接指定失败的回调(支持接收上一个then发生的错误)

Promise.all()

这可能是个很有用的方法,它可以统一处理多个Promise

Promise.all能将多个Promise实例包装成一个Promise实例

</>复制代码

  1. let Promise1 = new Promise(function(resolve, reject){})
  2. let Promise2 = new Promise(function(resolve, reject){})
  3. let Promise3 = new Promise(function(resolve, reject){})
  4. let p = Promise.all([Promise1, Promise2, Promise3])
  5. p.then(funciton(){
  6. // 三个都成功则成功
  7. }, function(){
  8. // 只要有失败,则失败
  9. })

这个组合后的Promise实例和普通实例一样,有三种状态,这里有组成它的几个小Promise的状态决定

1、当Promise1, Promise2, Promise3的状态都为成功态,则p为成功态;
2、当Promise1, Promise2, Promise3中有任意一个为失败态,则p为失败态;

Promise.race()

与all方法类似,也可以讲多个Promise实例包装成一个新的Promise实例

不同的是,all时大Promise的状态由多个小Promise共同决定,而race时由第一个转变状态的小Promise的状态决定,第一个是成功态,则转成功态,第一个失败态,则转失败态

Promise.resolve()

可以生成一个成功的Promise

Promise.resolve("成功")等同于new Promise(function(resolve){resolve("成功")})

Promise.resolve()

可以生成一个失败的Promise

Promise.reject("出错了")等同于new Promise((resolve, reject) => reject("出错了"))

上述用法不够详细,下面的代码会更容易理解 2. Why Promise?

以jquery的ajax为例(@1.5.0版本以前,后来jqery也引入了Promise的概念),看看从前我们是如何解决异步问题的。

</>复制代码

  1. $.get("url", {data: data}, function(result){
  2. console.log("成功", result)// 成功的回调,result为异步拿到的数据
  3. });

看起来还可以?

想象一个场景,当我们需要发送多个异步请求,而请求之间相互关联相互依赖,没有请求1就不会有请求2,没有请求2就不会有请求3........

这时我们需要这样写

</>复制代码

  1. $.get("url", {data: data}, function(result1){
  2. $.get("url", {data: result1}, function(result2){
  3. $.get("url", {data: result2}, function(result3){
  4. $.get("url", {data: result3}, function(result4){
  5. ......
  6. $.get("url", {data: resultn}, function(resultn+1){
  7. console.log("成功")
  8. }
  9. }
  10. }
  11. }
  12. });

这样的话,我们就掉入了传说中的回调地狱,万劫不复,不能自拔。

这种代码,难以维护和调试,一旦出现bug,牵一发而动全身。

下面我们看看Promise是如何解决的,我们以node中的fs访问文件举例

先创建三个相互依赖的txt文件

1.txt的内容:

</>复制代码

  1. 2.txt

2.txt的内容:

</>复制代码

  1. 3.txt

3.txt的内容:

</>复制代码

  1. 完成

js代码:

</>复制代码

  1. let readFile = require("fs").readFile; // 加载node内置模块fs 利用readFile方法异步访问文件
  2. function getFile(url){ // 创建一个读取文件方法
  3. return new Promise(function(resolve, reject){ // 返回一个Promise对象
  4. readFile(url, "utf8", function(err,data){ // 读取文件
  5. resolve(data) // 调用成功的方法
  6. })
  7. })
  8. }
  9. getFile("1.txt").then(function(data){ // then方法进行链式调用
  10. console.log(data) // 2.txt
  11. return getFile(data) //拿到了第一次的内容用来请求第二次
  12. }).then(function(data){
  13. console.log(data) // 3.txt
  14. return getFile(data) //拿到了第二次的内容用来请求第三次
  15. }).then(function(data){
  16. console.log(data) // 完成
  17. })

(这里我们先不必搞懂代码,下面会介绍具体用法)

看起来多了几行代码[尴尬],但我们通过创建一个读取函数返回一个Promise对象,再利用Promise自带的.then方法,将嵌套的异步代码弄得看起来像同步一样,这样的话,出现问题可以轻易的调试和修改。

3. What Promise?

接下来是本文的重头戏,根据PromiseA+(Promise的官方标准)动手实现一个180行左右代码的promise,功能可实现多数(then catch all race resolve reject),这里会将的比较详细,一步一步理清思路。

实现resolve、reject方法,then方法和状态机制

根据使用方法我们可以知道,Promise是一个需要接受一个执行器的构造函数,执行器提供两个方法,内部有状态机制,原型链上有then方法。

开始撸:

</>复制代码

  1. // myPromise
  2. function Promise(executor){ //executor是一个执行器(函数)
  3. let _this = this // 先缓存this以免后面指针混乱
  4. _this.status = "pending" // 默认状态为等待态
  5. _this.value = undefined // 成功时要传递给成功回调的数据,默认undefined
  6. _this.reason = undefined // 失败时要传递给失败回调的原因,默认undefined
  7. function resolve(value) { // 内置一个resolve方法,接收成功状态数据
  8. // 上面说了,只有pending可以转为其他状态,所以这里要判断一下
  9. if (_this.status === "pending") {
  10. _this.status = "resolved" // 当调用resolve时要将状态改为成功态
  11. _this.value = value // 保存成功时传进来的数据
  12. }
  13. }
  14. function reject(reason) { // 内置一个reject方法,失败状态时接收原因
  15. if (_this.status === "pending") { // 和resolve同理
  16. _this.status = "rejected" // 转为失败态
  17. _this.reason = reason // 保存失败原因
  18. }
  19. }
  20. executor(resolve, reject) // 执行执行器函数,并将两个方法传入
  21. }
  22. // then方法接收两个参数,分别是成功和失败的回调,这里我们命名为onFulfilled和onRjected
  23. Promise.prototype.then = function(onFulfilled, onRjected){
  24. let _this = this; // 依然缓存this
  25. if(_this.status === "resolved"){ // 判断当前Promise的状态
  26. onFulfilled(_this.value) // 如果是成功态,当然是要执行用户传递的成功回调,并把数据传进去
  27. }
  28. if(_this.status === "rejected"){ // 同理
  29. onRjected(_this.reason)
  30. }
  31. }
  32. module.exports = Promise // 导出模块,否则别的文件没法使用

注意:上面代码的命名不是随便起的,像onFulfilled和onRjected,是严格按照Promise/A+规范走的,不信你看图

这样我们就实现了第一步,可以创建Promise实例并使用then方法了,测试一下

</>复制代码

  1. let Promise = require("./myPromise") // 引入模块
  2. let p = new Promise(function(resolve, reject){
  3. resolve("test")
  4. })
  5. p.then(function(data){
  6. console.log("成功", data)
  7. },function(err){
  8. console.log("失败", err)
  9. })
  10. // 成功 test

再试试reject

</>复制代码

  1. let Promise = require("./myPromise") // 引入模块
  2. let p = new Promise(function(resolve, reject){
  3. reject("test")
  4. })
  5. p.then(function(data){
  6. console.log("成功", data)
  7. },function(err){
  8. console.log("失败", err)
  9. })
  10. // 失败 test

看起来不错,但回调函数是立即执行的,无法进行异步操作,比如这样是不行的

</>复制代码

  1. let p = new Promise(function(resolve, reject){
  2. setTimeout(function(){
  3. resolve(100)
  4. }, 1000)
  5. })
  6. p.then(function(data){
  7. console.log("成功", data)
  8. },function(err){
  9. console.log("失败", err)
  10. })
  11. // 不会输出任何代码

原因是我们在then函数中只对成功态和失败态进行了判断,而实例被new时,执行器中的代码会立即执行,但setTimeout中的代码将稍后执行,也就是说,then方法执行时,Promise的状态没有被改变依然是pending态,所以我们要对pending态也做判断,而由于代码可能是异步的,那么我们就要想办法把回调函数进行缓存,并且,then方法是可以多次使用的,所以要能存多个回调,那么这里我们用一个数组。

实现异步

在实例上挂两个参数

</>复制代码

  1. _this.onResolvedCallbacks = []; // 存放then成功的回调
  2. _this.onRejectedCallbacks = []; // 存放then失败的回调

then方法加一个pending时的判断

</>复制代码

  1. if(_this.status === "pending"){
  2. // 每一次then时,如果是等待态,就把回调函数push进数组中,什么时候改变状态什么时候再执行
  3. _this.onResolvedCallbacks.push(function(){ // 这里用一个函数包起来,是为了后面加入新的逻辑进去
  4. onFulfilled(_this.value)
  5. })
  6. _this.onRejectedCallbacks.push(function(){ // 同理
  7. onRjected(_this.reason)
  8. })
  9. }

下一步要分别在resolve和reject方法里加入执行数组中存放的函数的方法,修改一下上面的resolve和reject方法

</>复制代码

  1. function resolve(value) {
  2. if (_this.status === "pending") {
  3. _this.status = "resolved"
  4. _this.value = value
  5. _this.onResolvedCallbacks.forEach(function(fn){ // 当成功的函数被调用时,之前缓存的回调函数会被一一调用
  6. fn()
  7. })
  8. }
  9. }
  10. function reject(reason) {
  11. if (_this.status === "pending") {
  12. _this.status = "rejected"
  13. _this.reason = reason
  14. _this.onRejectedCallbacks.forEach(function(fn){// 当失败的函数被调用时,之前缓存的回调函数会被一一调用
  15. fn()
  16. })
  17. }
  18. }

现在可以执行异步任务了,也可以多次then了,一个穷人版Promise就完成了,

处理错误

上面的代码虽然能用,但经不起考验,真正的Promise如果在实例中抛出错误,应该走reject:

</>复制代码

  1. new Promise(function(resolve, reject){
  2. throw new Error("错误")
  3. }).then(function(){
  4. },function(err){
  5. console.log("错误:", err)
  6. })
  7. // 错误: Error: 错误

我们实现一下,思路很简单,在执行器执行时进行thy catch

</>复制代码

  1. try{
  2. executor(resolve, reject)
  3. }catch(e){ // 如果捕获发生异常,直接调失败,并把参数穿进去
  4. reject(e)
  5. }

实现then的链式调用(难点)

上面说过了,then可以链式调用,也是这一点让Promise十分好用,当然这部分源码也比较复杂

我们知道jquery实现链式调用是return了一个this,但Promise不行,为什么不行?

正宗的Promise是这样的套路:

</>复制代码

  1. let p1 = new Promise(function(resolve, reject){
  2. resolve()
  3. })
  4. let p2 = p1.then(function(data){ //这是p1的成功回调,此时p1是成功态
  5. throw new Error("错误") // 如果这里抛出错误,p2应是失败态
  6. })
  7. p2.then(function(){
  8. },function(err){
  9. console.log(err)
  10. })
  11. // Error: 错误

如果返回的是this,那么p2跟p1相同,固状态也相同,但上面说了,Promise的成功态和失败态不能相互转换,那就不会得到p1成功而p2失败的效果,而实际上是可能发生这种情况的。

所以Promise的then方法实现链式调用的原理是:返回一个新的Promise

在then方法中先定义一个新的Promise,取名为promise2(官方规定的),然后在三种状态下分别用promise2包装一下,在调用onFulfilled时用一个变量x(规定的)接收返回值,trycatch一下代码,没错就调resolve传入x,有错就调reject传入错误,最后再把promise2给return出去,就可以进行链式调用了,,,,但是!

</>复制代码

  1. // 改动then
  2. let promise2;
  3. if (_this.status === "resolved") {
  4. promise2 = new Promise(function (resolve, reject) {
  5. // 可以凑合用,但是是有很多问题的
  6. try {
  7. let x = onFulfilled(_this.value)
  8. resolve(x)
  9. } catch (e) {
  10. reject(e)
  11. }
  12. })
  13. }
  14. if (_this.status === "rejected") {
  15. promise2 = new Promise(function (resolve, reject) {
  16. // 可以凑合用,但是是有很多问题的
  17. try {
  18. let x = onRjected(_this.reason)
  19. resolve(x)
  20. } catch (e) {
  21. reject(e)
  22. }
  23. })
  24. }
  25. if(_this.status === "pending"){
  26. promise2 = new Promise(function (resolve, rejec
  27. _this.onResolvedCallbacks.push(function(){
  28. // 可以凑合用,但是是有很多问题的
  29. try {
  30. let x = onFulfilled(_this.value)
  31. resolve(x)
  32. } catch (e) {
  33. reject(e)
  34. }
  35. })
  36. _this.onRejectedCallbacks.push(function(){
  37. // 可以凑合用,但是是有很多问题的
  38. try {
  39. let x = onRjected(_this.reason)
  40. resolve(x)
  41. } catch (e) {
  42. reject(e)
  43. }
  44. })
  45. })
  46. }
  47. return promise2

这里我先解释一下x的作用再说为什么不行,x是用来接收上一次then的返回值,比如这样

</>复制代码

  1. let p = new Promise(function(resolve, reject){
  2. resolve(data)
  3. })
  4. p.then(function(data){
  5. return xxx // 这里返回一个值
  6. }, function(){
  7. }).then(function(data){
  8. console.log // 这里会接收到xxx
  9. }, function(){
  10. })
  11. // 以上代码中第一次then的返回值就是源码内第一次调用onRjected的返回值,可以用一个x来接收

接下来说问题,上面这样看起来是符合逻辑的,并且也确实可以链式调用并接受到,但我们在写库,库就要经得起考验,把容错性提到最高,要接受使用者各种新(cao)奇(dan)操作,所谓有容nai大。可能性如下:

1、前一次then返回一个普通值,字符串数组对象这些东西,都没问题,只需传给下一个then,刚才的方法就够用。

2、前一次then返回的是一个Promise,是正常的操作,也是Promise提供的语法糖,我们要想办法判断到底返回的是啥。

3、前一次then返回的是一个Promise,其中有异步操作,也是理所当然的,那我们就要等待他的状态改变,再进行下面的处理。

4、前一次then返回的是自己本身这个Promise

</>复制代码

  1. var p1 = p.then(function(){// 这里得用var,let由于作用域的原因会报错undefined
  2. return p1
  3. })

5、前一次then返回的是一个别人自己随便写的Promise,这个Promise可能是个有then的普通对象,比如{then:"哈哈哈"},也有可能在then里故意抛错(这种蛋疼的操作我们也要考虑进去)。比如他这样写

</>复制代码

  1. let promise = {}
  2. Object.defineProperty(promise,"then",{
  3. value: function(){
  4. throw new Error("报错气死你")
  5. }
  6. })
  7. // 如果返回这东西,我们再去调then方法就肯定会报错了

6、调resolve的时候再传一个Promise下去,我们还得处理这个Promise。

</>复制代码

  1. p.then(function(data) {
  2. return new Promise(function(resolve, reject) {
  3. resolve(new Promise(function(resolve,reject){
  4. resolve(1111)
  5. }))
  6. })
  7. })

7、可能既调resolve又调reject,得忽略后一个。

8、光then,里面啥也不写。

。。

稍等,我先吐一会。。。

好了咱们调整心情继续撸,其实这一系列的问题,很多都是相关的,只要根据规范,都可以顺利解决,接上面的代码,先干三件事

1、问题7是最好解决的,如果没传resolve和reject,我们就给他一个。

2、官方规范规定了一件事

简单说就是为免在测试中出问题onFulfilled和onRejected要异步执行,我们就让他异步执行

3、问题1-7,我们可以采取统一的觉得方案,定义一个函数来判断和处理这一系列的情况,官方给出了一个叫做resolvePromise的函数

再看then方法

</>复制代码

  1. Promise.prototype.then = function (onFulfilled, onRjected) {
  2. //成功和失败默认不传给一个函数,解决了问题8
  3. onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) {
  4. return value;
  5. }
  6. onRjected = typeof onRjected === "function" ? onRjected : function (err) {
  7. throw err;
  8. }
  9. let _this = this;
  10. let promise2; //返回的promise
  11. if (_this.status === "resolved") {
  12. promise2 = new Promise(function (resolve, reject) {
  13. // 当成功或者失败执行时有异常那么返回的promise应该处于失败状态
  14. setTimeout(function () {// 根据规范让那俩家伙异步执行
  15. try {
  16. let x = onFulfilled(_this.value);//这里解释过了
  17. // 写一个方法统一处理问题1-7
  18. resolvePromise(promise2, x, resolve, reject);
  19. } catch (e) {
  20. reject(e);
  21. }
  22. })
  23. })
  24. }
  25. if (_this.status === "rejected") {
  26. promise2 = new Promise(function (resolve, reject) {
  27. setTimeout(function () {
  28. try {
  29. let x = onRjected(_this.reason);
  30. resolvePromise(promise2, x, resolve, reject);
  31. } catch (e) {
  32. reject(e);
  33. }
  34. })
  35. })
  36. }
  37. if (_this.status === "pending") {
  38. promise2 = new Promise(function (resolve, reject) {
  39. _this.onResolvedCallbacks.push(function () {
  40. setTimeout(function () {
  41. try {
  42. let x = onFulfilled(_this.value);
  43. resolvePromise(promise2, x, resolve, reject);
  44. } catch (e) {
  45. reject(e)
  46. }
  47. })
  48. });
  49. _this.onRejectedCallbacks.push(function () {
  50. setTimeout(function () {
  51. try {
  52. let x = onRjected(_this.reason);
  53. resolvePromise(promise2, x, resolve, reject);
  54. } catch (e) {
  55. reject(e);
  56. }
  57. })
  58. });
  59. })
  60. }
  61. return promise2;
  62. }

接下来看看resolvePromise该怎么写

</>复制代码

  1. function resolvePromise(promise2, x, resolve, reject) {
  2. // 接受四个参数,新Promise、返回值,成功和失败的回调
  3. // 有可能这里返回的x是别人的promise
  4. // 尽可能允许其他乱写
  5. if (promise2 === x) { //这里应该报一个类型错误,来解决问题4
  6. return reject(new TypeError("循环引用了"))
  7. }
  8. // 看x是不是一个promise,promise应该是一个对象
  9. let called; // 表示是否调用过成功或者失败,用来解决问题7
  10. //下面判断上一次then返回的是普通值还是函数,来解决问题12
  11. if (x !== null && (typeof x === "object" || typeof x === "function")) {
  12. // 可能是promise {},看这个对象中是否有then方法,如果有then我就认为他是promise了
  13. try {
  14. let then = x.then;// 保存一下x的then方法
  15. if (typeof then === "function") {
  16. // 成功
  17. //这里的y也是官方规范,如果还是promise,可以当下一次的x使用
  18. //用call方法修改指针为x,否则this指向window
  19. then.call(x, function (y) {
  20. if (called) return //如果调用过就return
  21. called = true
  22. // y可能还是一个promise,在去解析直到返回的是一个普通值
  23. resolvePromise(promise2, y, resolve, reject)//递归调用,解决了问题6
  24. }, function (err) { //失败
  25. if (called) return
  26. called = true
  27. reject(err);
  28. })
  29. } else {
  30. resolve(x)
  31. }
  32. } catch (e) {
  33. if (called) return
  34. called = true;
  35. reject(e);
  36. }
  37. } else { // 说明是一个普通值1
  38. resolve(x); // 表示成功了
  39. }
  40. }

测试一下

PromiseA+提供了测试库promises-aplus-tests,github上明确讲解了使用方法

公开一个适配器接口:

</>复制代码

  1. Promise.deferred = function () {
  2. let dfd = {};
  3. dfd.promise = new Promise(function (resolve, reject) {
  4. dfd.resolve = resolve;
  5. dfd.reject = reject;
  6. });
  7. return dfd
  8. }

用命令行: promises-aplus-tests myPromise.js

经过一系列测试得到结果

</>复制代码

  1. 872 passing (18s)

证明了我们的promise是完全符合规范的!

其他方法

除了最重要的then方法,Promise还有很多方法,但都不难,这里一次性介绍一遍

</>复制代码

  1. // 捕获错误的方法,在原型上有catch方法,返回一个没有resolve的then结果即可
  2. Promise.prototype.catch = function (callback) {
  3. return this.then(null, callback)
  4. }
  5. // 解析全部方法,接收一个Promise数组promises,返回新的Promise,遍历数组,都完成再resolve
  6. Promise.all = function (promises) {
  7. //promises是一个promise的数组
  8. return new Promise(function (resolve, reject) {
  9. let arr = []; //arr是最终返回值的结果
  10. let i = 0; // 表示成功了多少次
  11. function processData(index, y) {
  12. arr[index] = y;
  13. if (++i === promises.length) {
  14. resolve(arr);
  15. }
  16. }
  17. for (let i = 0; i < promises.length; i++) {
  18. promises[i].then(function (y) {
  19. processData(i, y)
  20. }, reject)
  21. }
  22. })
  23. }
  24. // 只要有一个promise成功了 就算成功。如果第一个失败了就失败了
  25. Promise.race = function (promises) {
  26. return new Promise(function (resolve, reject) {
  27. for (var i = 0; i < promises.length; i++) {
  28. promises[i].then(resolve,reject)
  29. }
  30. })
  31. }
  32. // 生成一个成功的promise
  33. Promise.resolve = function(value){
  34. return new Promise(function(resolve,reject){
  35. resolve(value);
  36. })
  37. }
  38. // 生成一个失败的promise
  39. Promise.reject = function(reason){
  40. return new Promise(function(resolve,reject){
  41. reject(reason);
  42. })
  43. }

结语:Promise是异步的较好的解决方案之一,通过对源码的解析,对Promise甚至js异步都有了深刻的理解。Promise已经诞生很久了,如果你还不了解它,那你已经很落后了,抓紧时间上车。程序世界一日千里,作为程序员,要主动拥抱变化。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93734.html

相关文章

  • 细解JavaScript ES7 ES8 ES9 新特性

    摘要:定期召开会议,会议由会员公司的代表与特邀专家出席。新版本将会包含每年截止时间之前完成的所有特性。它引入了一个新的构造函数和具有辅助函数的命名空间对象。 导言:ECMAScript的演化不会停止,但是我们完全没必要害怕。除了ES6这个史无前例的版本带来了海量的信息和知识点以外,之后每年一发的版本都仅仅带有少量的增量更新,一年更新的东西花半个小时就能搞懂了,完全没必要畏惧。本文将带您花大约...

    Youngs 评论0 收藏0
  • ES6Promise:要优雅,也要浪漫

    摘要:就算改变已经发生了,即使再对对象添加回调函数,也会立即得到这个结果。方法接收个参数,第一个参数是状态的回调函数,第二个参数可选是状态的回调函数。简单来讲,就是能把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。 在ECMAScript 6标准中,Promise被正式列为规范,Promise,字面意思就是许诺,承诺,嘿,听着是不是很浪漫的说?我们来探究一下这个浪...

    weizx 评论0 收藏0
  • ta竟然用Node.js对这些beauty图做了这些事。。。

    摘要:做了什么一个用于爬取上妹子图片的爬虫。于是观察浏览器正常浏览行为。在请求头中设置和。解决该问题断线继续下载图片下载个文件时,经常断线。应该是网站的饭爬虫机制起了作用,暂时无法解决。于是在保存图片时会先判断图片是否存在。 做了什么 一个用于爬取www.nvshens.com上妹子图片的爬虫。如有侵权,马上关闭showImg(https://segmentfault.com/img/bVR...

    mayaohua 评论0 收藏0
  • 使用Pandas&NumPy进行数据清洗6大常用方法

    摘要:在这个教程中,我们将利用的和包来进行数据清洗。在很多情况下,使用唯一的值作为索引值识别数据字段是非常有帮助的。清洗数据字段到现在为止,我们移除了不必要的列并改变了我们的索引变得更有意义。 作者:xiaoyu微信公众号:Python数据科学知乎:Python数据分析师 数据科学家花了大量的时间清洗数据集,并将这些数据转换为他们可以处理的格式。事实上,很多数据科学家声称开始获取和清洗数据...

    siberiawolf 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<