JS基础 Promise

Posted wgchen~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JS基础 Promise相关的知识,希望对你有一定的参考价值。

阅读目录

Promise

javascript 中存在很多异步操作,Promise 将异步操作队列化,按照期望的顺序执行,返回符合预期的结果。可以通过链式调用多个 Promise 达到我们的目的。

Promise 在各种开源库中已经实现,现在标准化后被浏览器默认支持。

Promise 是一个拥有 then 方法的对象或函数。

问题探讨

下面通过多个示例来感受一下不使用 promise 时,处理相应问题的不易,及生成了不便阅读的代码。

定时嵌套

下面是一个定时器执行结束后,执行另一个定时器,这种嵌套造成代码不易阅读

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>wgchen</title>
</head>

<style>
  div 
    width: 100px;
    height: 100px;
    background: yellowgreen;
    position: absolute;
  
</style>

<body>
  <div></div>
</body>

<script> 
  function interval(callback, delay = 100) 
    let id = setInterval(() => callback(id), delay);
  
  
  const div = document.querySelector("div");
  
  interval(timeId => 
    const left = parseInt(window.getComputedStyle(div).left);
    div.style.left = left + 10 + "px";
    if (left > 200) 
      clearInterval(timeId);
      interval(timeId => 
        const width = parseInt(window.getComputedStyle(div).width);
        div.style.width = width - 1 + "px";
        if (width <= 0) clearInterval(timeId);
      , 10);
    
  , 100);
</script>

</html>

图片加载

下面是图片后设置图片边框,也需要使用回调函数处理,代码嵌套较复杂

function loadImage(file, resolve, reject) 
  const image = new Image();
  image.src = file;
  image.onload = () => 
    resolve(image);
  ;
  image.onerror = () => 
    reject(new Error("load fail"));
  ;
  document.body.appendChild(image);


loadImage(
  "888.png",
  image => 
    image.style.border = "solid 5px red";
  ,
  error => 
    console.log(error);
  
);

加载文件

下面是异步加载外部JS文件,需要使用回调函数执行,并设置的错误处理的回调函数

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>wgchen</title>
  <link rel="shortcut icon" href="#"/>
</head>
<body>
  
</body>
<script> 

function load(file, resolve, reject) 
  const script = document.createElement("script");
  script.src = file;
  script.onload = resolve;
  script.onerror = reject;
  document.body.appendChild(script);


load(
  "hd.js",
  script => 
    console.log(`$script.path[0].src 加载成功`);
    hd();
  ,
  error => 
    console.log(`$error.srcElement.src 加载失败`);
  
);

</script>
</html>

hd.js

function hd() 
  console.log("hd function run");

如果要加载多个脚本时需要嵌套使用,下面 wgchen.js 依赖 hd.js,需要先加载 hd.js 后加载wgchen.js

不断的回调函数操作将产生回调地狱,使代码很难维护

wgchen.js

function wgchen() 
    console.log("wgchen function run");
    hd();

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>wgchen</title>
  <link rel="shortcut icon" href="#"/>
</head>
<body>
  
</body>

<script> 

function load(file, resolve, reject) 
  const script = document.createElement("script");
  script.src = file;
  script.onload = resolve;
  script.onerror = reject;
  document.body.appendChild(script);


load(
  "hd.js",
  script => 
    load(
      "wgchen.js",
      script => 
        wgchen();
      ,
      error => 
        console.log(`$error.srcElement.src 加载失败`);
      
    );
  ,
  error => 
    console.log(`$error.srcElement.src 加载失败`);
  
);

</script>
</html>

异步请求

使用传统的异步请求也会产生回调嵌套的问题,下在是获取wgchen的成绩,需要经过以下两步

1、根据用户名取得 wgchen 的编号
2、根据编号获取成绩

启动php服务

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>wgchen</title>
  <link rel="shortcut icon" href="#"/>
</head>
<body>
  
</body>

<script> 

function ajax(url, resolve, reject) 
  let xhr = new XMLHttpRequest();
  xhr.open("GET", url);
  xhr.send();
  xhr.onload = function() 
    if (this.status == 200) 
      resolve(JSON.parse(this.response));
     else 
      reject(this);
    
  ;


ajax("http://tt.cc/testData/user.php?name=wgchen", user => 
  // console.log(user.id);
  ajax(
    `http://tt.cc/testData/wgchen.php?id=$user["id"]`,
    response => 
      console.log(response);
    
  );
);

</script>
</html>

user.php

<?php

$user = [
    'name'=>$_GET['name'],
    'file'=>'user.php',
    'id'=>18
];

echo json_encode($user);

wgchen.php

<?php

$user = [
    'name'=>'wgchen',
    'file'=>'wgchen.php',
    'id'=>$_GET['id']
];

echo json_encode($user);

肯德基

下面是模拟肯德基吃饭的事情,使用 promise 操作异步的方式每个阶段会很清楚

let kfc = new Promise((resolve, reject) => 
  console.log("肯德基厨房开始做饭");
  resolve("我是肯德基,你的餐已经做好了");
);
let dad = kfc.then(msg => 
  console.log(`收到肯德基消息: $msg`);
  return 
    then(resolve) 
      setTimeout(() => 
        resolve("孩子,我吃了两秒了,不辣,你可以吃了");
      , 2000);
    
  ;
);
let son = dad.then(msg => 
  return new Promise((resolve, reject) => 
    console.log(`收到爸爸消息: $msg`);
    setTimeout(() => 
      resolve("妈妈,我和wgchen爸爸吃完饭了");
    , 2000);
  );
);
let ma = son.then(msg => 
  console.log(`收到孩子消息: $msg,事情结束`);
);

而使用以往的回调方式,就会让人苦不堪言

function notice(msg, then) 
  then(msg);

function meal() 
  notice("肯德基厨房开始做饭", msg => 
    console.log(msg);
    notice("我是肯德基,你的餐已经做好", msg => 
      console.log(`收到肯德基消息: $msg`);
      setTimeout(() => 
        notice("孩子,我吃了两秒了,不辣,你可以吃了", msg => 
          console.log(`收到爸爸消息: $msg`);
          setTimeout(() => 
            notice("妈妈,我和wgchen吃完饭了", msg => 
              console.log(`收到孩子消息: $msg,事情结束`);
            );
          , 2000);
        );
      , 2000);
    );
  );

meal();

异步状态

Promise 可以理解为承诺,就像我们去KFC点餐服务员给我们领取餐票,这就是承诺。如果餐做好了叫我们这就是成功,如果没有办法给我们做出食物这就是拒绝。

  • 一个 promise 必须有一个 then 方法用于处理状态改变

状态说明

Promise 包含 pending、fulfilled、rejected 三种状态

  • pending 指初始等待状态,初始化 promise 时的状态
  • resolve 指已经解决,将 promise 状态设置为 fulfilled
  • reject 指拒绝处理,将 promise 状态设置为 rejected
  • promise 是生产者,通过 resolve 与 reject 函数告之结果
  • promise 非常适合需要一定执行时间的异步任务
  • 状态一旦改变将不可更改

promise 是队列状态,就像体育中的接力赛,或多米诺骨牌游戏,状态一直向后传递,当然其中的任何一个promise也可以改变状态。

promise 没有使用 resolve 或 reject 更改状态时,状态为 pending

console.log(
  new Promise((resolve, reject) => )
);

当更改状态后

console.log(
  new Promise((resolve, reject) => 
    resolve("fulfilled");
  )
);

console.log(
  new Promise((resolve, reject) => 
    reject("rejected");
  )
);


promise 创建时即立即执行即同步任务,then 会放在异步微任务中执行,需要等同步任务执行后才执行。

let promise = new Promise((resolve, reject) => 
  resolve("fulfilled");
  console.log("wgchen");
);

promise.then(msg => 
  console.log(msg);
);

console.log("wgchen.blog.csdn.net");

  • promise 的 then、catch、finally的方法都是异步任务
  • 程序需要将主任务执行完成才会执行异步队列任务

const promise = new Promise(resolve => resolve("success"));

promise.then(alert);
alert("wgchen.blog.csdn.net");

promise.then(() => 
  alert("wgchen");
);

Promise 状态设置为 fulfilled ,然后执行 then 方法

下例在三秒后将 Promise 状态设置为 fulfilled ,然后执行 then 方法

new Promise((resolve, reject) => 
  setTimeout(() => 
    resolve("fulfilled");
  , 3000);
).then(
  msg => 
    console.log(msg);
  ,
  error => 
    console.log(error);
  
);

状态被改变后就不能再修改了,下面先通过 resolve 改变为成功状态,表示 promise 状态已经完成,就不能使用 reject 更改状态了

new Promise((resolve, reject) => 
  resolve("操作成功");
  reject(new Error("请求失败"));
).then(
  msg => 
    console.log(msg);
  ,
  error => 
    console.log(error);
  
);

动态改变

下例中 p2 返回了 p1 所以此时 p2 的状态已经无意义了,后面的 then 是对 p1 状态的处理。以上是关于JS基础 Promise的主要内容,如果未能解决你的问题,请参考以下文章

JS基础 Promise

js异步解决方案及promise基础

Promise

JS基础——Promise基本使用

JavaScript 标准内置对象Promise使用学习总结

进击Node.js基础