资讯专栏INFORMATION COLUMN

promise用法解析

fai1017 / 1122人阅读

摘要:这时,第二个方法指定的回调函数,就会等待这个新的对象状态发生变化。

1.概述

javascript是单线程语言(单线程/多线程、阻塞/非阻塞、同步、异步)参考此文章,所有的任务都是按顺序执行的,但是对于很耗时的操作采用异步的方式(前一个任务结束的时候,不是执行下一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行)参考此文章,js中异步编程的四种方法:回调函数、事件监听、发布/订阅、Promise对象,这里讨论promise的用法,promise是es6增加的内容,使得在异步编程中不用过多的嵌套回调函数,让异步操作变得更加简单

2.Promise介绍 2.1 Promise构造函数

Promise构造函数接收一个函数作为参数,此函数又有两个参数分别为resolve和reject,这两个参数也是函数

  const promise = new Promise(function(resolve, reject) {
  // 异步操作的代码
  if(success) {
    return resolve(data); // data为异步操作成功返回的数据
  } else {
    return reject(error); //data为失败时返回的数据
  }
})
2.2 resolve和reject

promise对象类似于一个容器(也可以说是一个状态机),里面包含着异步操作,异步操作会有两种结果:成功或失败。当异步操作成功就会将pending(执行中状态)转为fulfilled(成功状态)同时触发resolve函数,用来将操作成功后得到的结果传递出去;当异步操作失败就会将pending(执行中状态)转为reject(拒绝状态)同时触发reject函数,用来将操作失败后报出的错误传递出去

const promise = new Promise(function(resolve, reject) {
  // 异步操作的代码
  if(success) {
    return resolve(data); // data为异步操作成功返回的数据
  } else {
    return reject(error); //data为失败时返回的数据
  }
})

我们来看下面代码:

  function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn1")
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn2")
      },2000)
    })
  }
fn1().then(fn2)

输出为:
fn1
fn2函数不执行,这个时候我们需要调用resolve函数以便执行then()方法中的回调函数fn2

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn1")
        resolve("fn1")
      },1000)
    })
  }

输出为:
fn1
fn2
如果我们在fn1中调用reject函数时会出现什么情况呢?

function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn1")
        // resolve("fn1")
        reject("错误"+"fn1")
      },1000)
    })
  }
  fn1().then(fn2).then((data) => {
    console.log(data)
  })

输出为:



提示错误未捕获,所以需要在then()方法中添加第二个回到函数来处理出错信息

  fn1().then(fn2).then((data) => {
    console.log(data)
  }, (err) => {
    console.log(err)
  })

输出为:


2.3 then()方法

当promise实例生成以后,后面跟then方法,其的第一个回调函数来处理resolve函数传递的数据,第二个回调函数来处理reject函数传递的数据,以上的流程用代码展示如下

promise
  .then(function(data){
    //拿到返回的数据之后做一些处理
    console.log(data)
  }, function(error) {
    //返回失败时做一些处理
    console.log(error)
  })
2.3.1 then()的返回值

then()方式是Promise实例的方法,此then方法定义在原型对象(Promise.prototype)上,then()方法的返回值是一个新的Promise实例(不是原来那个Promise,原来那个Promise已经承诺过)所以我们可以采用链式的写法

var p1 = new Promise( (resolve, reject) => {
    setTimeout(() => resolve("p1"), 10);
});

p1.then( ret => {
    console.log(ret);
    return "then1";
}).then( ret => {
    console.log(ret);
    return "then2";
}).then( ret => {
    console.log(ret);
});

执行顺序:
p1>then1>then2
从第二个then()方法开始,它们的resolve中的参数就是前一个then()中的resolve的return语句的返回值
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);}).then(function funcA(comments) {
  console.log("resolved: ", comments);}, function funcB(err){
  console.log("rejected: ", err);}) 

上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用funcA,如果状态变为rejected,就调用funcB。
如果采用箭头函数,上面的代码可以写得更简洁

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err));

promise链式写法如下:

  function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn1")
        resolve("fn1")
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn2")
        resolve("fn2")
      },2000)
    })
  }
  function fn3() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn3")
        resolve("fn3")
      },3000)
    })
  }
  function fn4() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn4")
        resolve("fn4")
      },4000)
    })
  }
  fn1().then(fn2).then(fn3).then((data) => {
    console.log(data)
  }, (err) => {
    console.log(err)
  })

依次输出为:fn1>fn2>fn3
同时我们还可以更改执行的先后顺序

  function fn1() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn1")
        reject(false)
      },1000)
    })
  }
  function fn2() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn2")
        resolve("fn2")
      },2000)
    })
  }
  function fn3() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn3")
        resolve("fn3")
      },3000)
    })
  }
  function fn4() {
    return new Promise(function(resolve, reject){
      setTimeout(() => {
        console.log("fn4")
        resolve("fn4")
      },4000)
    })
  }
  fn1().then(fn2).then(fn3).then((data) => {
    console.log(data)
  }, (err) => {
    if(err == false){
      fn3().then(fn4)
    }
  })

输出为:fn1>fn3>fn4

2.4 catch()方法

我们在开发时倾向于用catch()方法来处理异常,而不是在then()方法里写入第二个回调函数,这种写法类似于.then(null, rejection)

promise
  .then(function(data){
    //拿到返回的数据之后做一些处理
    console.log(data)
  })
  .catch(function(error) {
    //返回失败时做一些处理
    console.log(error)
  }

为什么要采用这么catch()方法呢?我们来看一个例子:

const promise = new Promise((resolve,reject) => {
  console.log(1)
  resolve("成功")
})
promise
  .then((data) => {
    console.log(data)
    console.log(a)
  }, (err) => {
    console.log(err)
  })

输出为:
1
成功
但是没有捕捉到回调函数里a未定义
这个时候我们来该写以上代码
1
成功
ReferenceError: a is not defined
Promise对象的Error对象具有冒泡性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获

var p = new Promise( (resolve, reject) => {
    setTimeout(() => resolve("p1"), 10);
});

p.then( ret => {
    console.log(ret);
    throw new Error("then1");
    return "then1";
}).then( ret => {
    console.log(ret);
    throw new Error("then2");
    return "then2";
}).catch( err => {
    // 可以捕抓到前面的出现的错误。
    console.log(err.toString());
});

输出为:
p1
Error: then1

2.4.1 catch()返回值

既然catch()是.then(null, rejection)的别名,那么catch()就会返回一个Promise对象,因此在后面还可以接着调用then方法

var p = new Promise((resolve, reject) => {
    resolve(x + 2);
});
p.then( () => {
    console.log("nothing");
}).catch( err => {
    console.log(err.toString());
    return "catch";
}).then( ret => {
    console.log(ret);
});

输出为:
ReferenceError: x is not defined
catch
当出错时,catch会先处理之前的错误,然后通过return语句,将值继续传递给后一个then方法中,如果没有报错,则跳过catch,示例如下:

var p = new Promise((resolve, reject) => {
    resolve("p");
});
p.then( ret => {
    console.log(ret);
    return "then1";
}).catch( err => {
    console.log(err.toString());
    return "catch";
}).then( ret => {
    console.log(ret);
});
3. promise用法解析 3.1 用Promise实现ajax操作
const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log("Contents: " + json);
}, function(error) {
  console.error("出错了", error);
});
3.2 promise和ajax方法结合
var http = {
    get: function(url) {
        var promise = new Promise(function(resolve, reject) {
            $.ajax({
                url: url,
                method: "get",
                success: function(data) {
                    resolve(data);
                },
                error: function(xhr, statusText) {
                    reject(statusText);
                }
            });
        });
        return promise;
    }
};
http.get("data.php").then(function(data) {
    document.write(data);
}, function(err) {
    document.write(err);
});
3.3 用promise实现异步加载图片的例子
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error("Could not load image at " + url));
    };

    image.src = url;
  });
}
var loadImage1 = loadImageAsync(url);
loadImage1.then(function success() {
    console.log("success");
}, function fail() {
    console.log("fail");
});
3.4 promise和axios方法结合
function fetch(url, params) {
  return new Promise((resolve, reject) => {
    axios.post(url, params)
      .then(response => {
        resolve(response.data);
      }, error => {
        reject(error);
      })
      .catch(error => {
        reject(error)
      })
  })
}

function lineStatus(params) {
  return fetch("/ProductionLine/GetStatus", params)
}
function statisticsData(params) {
  return fetch("/ProductionLine/GetWeightStatistics", params)
}

      lineStatus(this.lineId)
        .then((result) => {
          this.deviceStatus.run = result.TotleMoveMotor
          this.deviceStatus.stop = result.TotleStopMotor
          this.deviceStatus.lost = result.TotleLoseMotor
          this.deviceStatus.alarm = result.TotleAlarmMotor
          this.ProductionStatus = result.ProductionStatus
          console.log(result)
        })
        .catch((error) => {
          console.log("瓦特了...(;′⌒`)")
        })

      statisticsData(this.lineId)
        .then((result) => {
          this.outputData.totalOutput = result.MainConveyorModel.OutPut
          this.outputData.instantWeight = result.MainConveyorModel.InstantWeight
          this.outputData.runningTime = result.MainConveyorModel.RunningTime
          this.outputData.motorLoad = result.MainConveyorModel.MotorLoad
          // console.log(result)
        })
        .catch((error) => {
          console.log("瓦特了...(;′⌒`)")
        })

以上是Promise在开发中常见的用法,参考了以下几篇文章,特此感谢作者

Promise 对象[阮一峰]

[es6系列]学习Promise

阮一峰的ES6---Promise对象

对Promise中的resolve,reject,catch的理解

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

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

相关文章

  • 浅述Promise用法(一)

    摘要:概述是异步编程的一种解决方案,很好的解决了传统异步编程中的回调地狱问题。语法中阐述,有三种状态,分别是进行中已完成已失败。方法使用要使用方法我们首先要创建实例,也就是说我们要一个对象。 1. Promise概述 promise是异步编程的一种解决方案,很好的解决了传统异步编程中的回调地狱问题。同时我们可以把promise可以理解为一个容器,这个容器里面存放着一些未来才会结束的事件(通常...

    BakerJ 评论0 收藏0
  • ES6中Promise 承诺对象封装异步操作解析

    摘要:有了对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,对象提供统一的接口,使得控制异步操作更加容易。它的作用是为实例添加状态改变时的回调函数。这时,第二个方法指定的回调函数,就会等待这个新的对象状态发生变化。 Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。它由社区最早出和实现,ES6 将其写进了语言标准,统一了...

    qianfeng 评论0 收藏0
  • Service Worker 基本用法

    摘要:在服务工作线程中,延长事件的寿命从而阻止浏览器在事件中的异步操作完成之前终止服务工作线程。在事件中,它延迟将视为已激活的,直到传递的被成功地。这主要用于确保任何功能事件不会被分派到对象,直到它升级数据库模式并删除过期的缓存条目。 看了很多介绍Service Worker的,看得都挺模糊的,所以决定自己写一篇文件整理一下思路。 一、Service Worker API 名词区分 1、Se...

    Charlie_Jade 评论0 收藏0
  • Eventloop不可怕,可怕的是遇上Promise

    摘要:就是每次传入的函数最后是的任务之后,开始执行,可以看到此时会批量执行中的函数,而且还给这些中回调函数放入了一个这个很显眼的函数之中,表示这些回调函数是在微任务中执行的。下一模块会对此微任务中的插队行为进行详解。 有关Eventloop+Promise的面试题大约分以下几个版本——得心应手版、游刃有余版、炉火纯青版、登峰造极版和究极变态版。假设小伙伴们战到最后一题,以后遇到此类问题,都是...

    olle 评论0 收藏0

发表评论

0条评论

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