Maison  >  Article  >  interface Web  >  Qu’est-ce que la Promesse ? Introduction aux promesses

Qu’est-ce que la Promesse ? Introduction aux promesses

不言
不言avant
2018-10-17 14:55:133557parcourir

Le contenu de cet article porte sur qu'est-ce que Promise ? L'introduction de Promise a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.

En fait, je voulais écrire sur l'utilisation de Promise depuis longtemps. L'une est qu'il est souvent utilisé dans le processus de codage lui-même, et l'autre est que parfois des amis rencontreront des problèmes lors de son utilisation.

Promise est en effet l'une des fonctionnalités de l'API d'ES6 qui a vraiment eu le plus grand impact sur la façon dont vous écrivez du JS.
Cet article est un résumé du processus d'utilisation réel
En regardant l'heure de création du fichier le 09/10/2017, la procrastination est vraiment terrible. . . Nous devons encore renforcer notre capacité d’exécution ! N'oubliez pas votre intention initiale, allez !

Préface et& Concepts de base

Promise est une solution à l'asynchronisme de JS Par rapport aux fonctions de rappel traditionnelles, Promise peut résoudre le problème de l'imbrication sérieuse de plusieurs rappels.

L'objet Promise représente une opération asynchrone et a trois états : en attente, réalisé ou rejeté. La transition d'état ne peut être qu'en attente -> en attente -> .

9918a60989ece9c0e9fb9608baca383f

Je pense personnellement qu'expliquer Promise doit en fait être divisé en deux parties

  1. Pour le Promesse constructeur Mode d'emploi.

  2. Quelques méthodes sur l'objet prototype Promise.

Constructeur Promise

ES6 stipule que l'objet Promise est un constructeur utilisé pour générer des instances Promise.

Le constructeur Promise accepte une fonction comme paramètre. Les deux paramètres de la fonction sont résoudre et rejeter. Ce sont deux fonctions fournies par le moteur JavaScript et n'ont pas besoin d'être déployées par vous-même.

Le but de la fonction de résolution est de changer l'état de l'objet Promise de « inachevé » à « réussi » (c'est-à-dire de en attente à rempli). Elle est appelée lorsque l'opération asynchrone est réussie, et. le résultat de l'opération asynchrone est utilisé comme Les paramètres sont transmis ; la fonction de la fonction
reject est de changer le statut de l'objet Promise de "inachevé" à "échoué" (c'est-à-dire de en attente à rejeté) , et est appelé lorsque l'opération asynchrone échoue et signale l'opération asynchrone. L'erreur est passée en paramètre.

Le code suivant crée une instance Promise. Après que

function request() {
  return new Promise((resolve, reject) => {
    /* 异步操作成功 */
    setTimeout(() => {
      resolve("success");
    }, 1000);
    // 取消注释这里可以体现,Promise 的状态一旦变更就不会再变化的特性
    // reject('error');
  });
}

ait reçu le

request()
  .then(result => {
    console.info(result);
  })
  .catch(error => {
    console.info(error);
  });

ci-dessus, en plus d'utiliser catch pour capturer les erreurs, vous pouvez également utiliser la méthode new Promise() pour spécifier then et resolve La fonction de rappel reject peut également atteindre l'objectif de détecter les erreurs.

request().then(
  result => {
    console.info(result);
  },
  error => {
    console.info(error);
  }
);
Méthodes sur Prototype

Promise.prototype.then()

p.then(onFulfilled, onRejected)
Alors la méthode est une méthode définie sur Promise.prototype, comme ci-dessus Le même exemple , il y a deux paramètres, la fonction de rappel remplie et la fonction de rappel rejetée, le deuxième paramètre est facultatif.

Deux points clés :

  1. La valeur de retour de la méthode then est une nouvelle instance

    , donc pour l'appelant, obtenez un Promise Object renvoie toujours a Promise après avoir appelé then, et son comportement est lié à la valeur de retour de la fonction de rappel dans then. Comme suit : Promise

  • Si la fonction de rappel dans then renvoie une valeur, alors la promesse renvoyée d'ici là deviendra l'état accepté et la valeur renvoyée sera considérée comme état accepté. La valeur du paramètre de la fonction de rappel.

  • Si la fonction de rappel renvoie ensuite une erreur, alors la promesse renvoyée d'ici là deviendra l'état rejeté et l'erreur renvoyée sera utilisée comme valeur de paramètre de la fonction de rappel dans l’État rejeté.

  • Si la fonction de rappel dans renvoie ensuite une promesse qui est déjà dans l'état d'acceptation, alors la promesse renvoyée d'ici là deviendra également l'état d'acceptation, et les paramètres de la fonction de rappel dans l'état d'acceptation de cette promesse sera La valeur est utilisée comme valeur de paramètre de la fonction de rappel d'état d'acceptation de la promesse renvoyée.

  • Si la fonction de rappel renvoie alors une promesse qui est déjà dans un état rejeté, alors la promesse renvoyée d'ici là deviendra également un état rejeté, et les paramètres de la fonction de rappel de l'état rejeté de cette promesse sera La valeur est utilisée comme valeur de paramètre de la fonction de rappel de l'état de rejet de la promesse renvoyée.

  • Si la fonction de rappel dans then renvoie une promesse dans un état en attente, alors l'état de la promesse renvoyée à ce moment-là est également en attente, et son état final est le même que celui de celle-ci. Promesse. ;En même temps, les paramètres de la fonction de rappel appelée lorsqu'elle devient l'état final sont les mêmes que les paramètres de la fonction de rappel lorsque la promesse devient l'état final.

  1. Appel en chaîne. Convertissez le format de code des rappels imbriqués en un mode vertical d'appels chaînés.

Par exemple, formulaire de rappel : Un exemple d'enfer de rappel

a(a1 => {
  b(a1, b1 => {
    c(b1, c1 => {
      d(c1, d1 => {
        console.log(d1);
      });
    });
  });
});
Une telle expansion horizontale peut être modifiée en (a, b, c, d) sont tous return La structure verticale de la fonction de Promise

a()
  .then(b)
  .then(c)
  .then(d)
  .then(d1 => {
    console.log(d1);
  });
//===== 可能上面的例子并不太好看 ===下面这样更直观
a()
  .then(a1 => b(a1))
  .then(b1 => c(b1))
  .then(c1 => d(c1))
  .then(d1 => {
    console.log(d1);
  });
semble beaucoup plus propre.

Promise.prototype.catch()

En plus de then(), il existe également une méthode catch() sur la chaîne de prototypes Promise.prototype, qui est la fonction de gestion des situations de rejet .

其实 它的行为与调用 Promise.prototype.then(undefined, onRejected) 相同。 (事实上, calling obj.catch(onRejected) 内部 calls obj.then(undefined, onRejected)).

// 1.
request().then(
  result => {
    console.info(result);
  },
  error => {
    console.info(error);
  }
);

// 2.
request()
  .then(result => {
    console.info(result);
  })
  .catch(error => {
    console.info(error);
  });

如上这个例子:两种方式在使用,与结果基本上是等价的,但是 仍然推荐第二种写法,下面我会给出原因:

  1. 在 Promise 链中 Promise.prototype.then(undefined, onRejected),onRejected 方法无法捕获当前 Promise 抛出的错误,而后续的 .catch 可以捕获之前的错误。

  2. 代码冗余

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("reject");
  }, 1000);
})
  .then(
    result => {
      console.log(result + "1");
      throw Error(result + "1"); // 抛出一个错误
    },
    error => {
      console.log(error + ":1"); // 不会走到这里
    }
  )
  .then(
    result => {
      console.log(result + "2");
      return Promise.resolve(result + "2");
    },
    error => {
      console.log(error + ":2");
    }
  );
// reject1, Error: reject1:2

如果使用 .catch 方法,代码会简化很多,这样实际上是延长了 Promise 链

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("reject");
  }, 1000);
})
  .then(result => {
    console.log(result + "1");
    throw Error(result + "1"); // 抛出一个错误
  })
  .then(result => {
    console.log(result + "2");
    return Promise.resolve(result + "2");
  })
  .catch(err => {
    console.log(err);
  });
// reject1, Error: reject1:2

Promise.prototype.finally()

暂未完全成为标准的一部分,处于:Stage 4

finally() 方法返回一个 Promise,在执行 then() 和 catch() 后,都会执行finally指定的回调函数。(回调函数中无参数,仅仅代表 Promise 的已经结束

等同于使用 .then + .catch 延长了原有的 Promise 链的效果,避免同样的语句需要在 then() 和 catch() 中各写一次的情况。

mdn-Promise-finally

Promise 对象上的方法

Promise.all() 用来处理 Promise 的并发

Promise.all 会将多个 Promise 实例封装成一个新的 Promise 实例,新的 promise 的状态取决于多个 Promise 实例的状态,只有在全体 Promise 都为 fulfilled 的情况下,新的实例才会变成 fulfilled 状态。;如果参数中 Promise 有一个失败(rejected),此实例回调失败(rejecte),失败原因的是第一个失败 Promise 的结果。

举个例子:

Promise.all([
  new Promise(resolve => {
    setTimeout(resolve, 1000, "p1");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 2000, "p2");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 3000, "p3");
  })
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error);
  });
// [p1,p2,p3]

Promise.all([
  new Promise(resolve => {
    setTimeout(resolve, 1000, "p1");
  }),
  new Promise(resolve => {
    setTimeout(resolve, 2000, "p2");
  }),
  Promise.reject("p3 error")
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error);
  });
// p3 error

获取 cnode 社区的 精华贴的前十条内容

fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10")
  .then(res => res.json())
  .then(res => {
    const fetchList = res.data.map(item => {
      return fetch(`https://cnodejs.org/api/v1/topic/${item.id}`)
        .then(res => res.json())
        .then(res => res.data);
    });
    Promise.all(fetchList).then(list => {
      console.log(list);
    });
  });

Promise.race() 竞态执行

Promise.race 也会将多个 Promise 实例封装成一个新的Promise实例,只不过新的 Promise 的状态取决于最先改变状态的 Promise 实例的状态。

在前端最典型的一个用法是为 fetch api 模拟请求超时。

Promise.race([
  fetch("https://cnodejs.org/api/v1/topics?tab=good&limit=10").then(res =>
    res.json()
  ),
  new Promise((resolve, reject) => {
    setTimeout(reject, 1, "error");
  })
])
  .then(result => {
    console.info("then", result);
  })
  .catch(error => {
    console.info("catch", error); // 进入这里
  });

上述例子中只要请求 未在 1 毫秒内结束就会进入 .catch() 方法中,虽然不能将请求取消,但是超时模拟却成功了

Promise.resolve(value) && Promise.reject(reason)

这两个方法都能用来创建并返回一个新的 Promise , 区别是 Promise.resolve(value) 携带进新的 Promise 状态是 fulfilled。而 Promise.reject(reason) 带来的 rejected

有的时候可以用来简化一些创建 Promise 的操作如:

const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time));
// 这里创建一个 睡眠,并且打印的链
Promise.resolve()
  .then(() => {
    console.log(1);
  })
  .then(() => sleep(1000))
  .then(() => {
    console.log(2);
  })
  .then(() => sleep(2000))
  .then(() => {
    console.log(3);
  });

有时也用来 手动改变 Promise 链中的返回状态 ,当然这样实际上和 直接返回一个值,或者是 使用 throw Error 来构造一个错误,并无区别。到底要怎么用 就看个人喜好了

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("resolve"); // 1.
  }, 1000);
})
  .then(result => {
    return Promise.reject("reject1"); // 2.
  })
  .then(
    result => {
      return Promise.resolve(result + "2");
    },
    err => {
      return Promise.resolve(err); // 3.
    }
  )
  .then(res => {
    console.log(res); // 4.
  })
  .catch(err => {
    console.log(err + "err");
  });
// reject1

几个例子

下面来看几个例子:

关于执行顺序,具体可搜索,js 循环

new Promise((resolve, reject) => {
  console.log("step 1");
  resolve();
  console.log("step 2");
}).then(() => {
  console.log("step 3");
});
console.log("step 4");

// step 1, step 2, step 4 , step 3

在使用 Promise 构造函数构造 一个 Promise 时,回调函数中的内容就会立即执行,而 Promise.then 中的函数是异步执行的。

关于状态不可变更

let start;
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    start = Date.now();
    console.log("once");
    resolve("success");
  }, 1000);
});
p.then(res => {
  console.log(res, Date.now() - start);
});
p.then(res => {
  console.log(res, Date.now() - start);
});
p.then(res => {
  console.log(res, Date.now() - start);
});

Promise 构造函数只执行一次,内部状态一旦改变,有了一个值,后续不论调用多少次then()都只拿到那么一个结果。

关于好像状态可以变更

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("success");
  }, 1000);
});

const p2 = p1.then((resolve, reject) => {
  throw new Error("error");
});

console.log("p1", p1);
console.log("p2", p2);

setTimeout(() => {
  console.log("p1", p1);
  console.log("p2", p2);
}, 2000);

观察这一次的打印
第一次打印出两个 Promise 的时候都是 pending ,因为 p2 是基于 p1 的结果,p1 正在 pending ,立即打印出的时候肯定是 pending ;第二次打印的时候,因为 p1 的状态为 resolved ,p2 为 rejected ,这个并不是已经为 fulfilled 状态改变为 rejected ,而是 p2 是一个新的 Promise 实例,then() 返回新的 Promise 实例。

关于透传

Promise.resolve(11)
  .then(1)
  .then(2)
  .then(3)
  .then(res => {
    console.info("res", res);
  });
//   11

给 then 方法传递了一个非函数的值,等同于 then(null),会导致穿透的效果,就是直接过掉了这个 then() ,直到符合规范的 then() 为止。

Promise 的串行调用

使用 Array.reduce 方法串行执行 Promise

const sleep = (time = 0) => new Promise(resolve => setTimeout(resolve, time));
[1000, 2000, 3000, 4000].reduce((Promise, item, index) => {
  return Promise.then(res => {
    console.log(index + 1);
    return sleep(item);
  });
}, Promise.resolve());
// 在分别的等待时间后输出 1,2,3,4

这篇文章到这里就基本上结束了,相信 如果能理解上面的内容,并且在实际项目中使用的话。应该会让工作更高效吧,对于新的异步使用应该也会更加的得心应手。Promise 的使用相对简单,可能后续再出一篇如何实现一个 Promise 吧。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer