Comment mettre en œuvre une promesse complète
J'ai utilisé Promise, mais j'ai toujours l'impression de ne pas le comprendre. J'ai lu de nombreux articles, mais je ne comprends toujours pas le principe de mise en œuvre de Promise. Plus tard, j'ai lu les articles et débogué le code, et finalement j'ai lentement commencé à le comprendre, mettons en œuvre une promesse selon notre propre compréhension.
Pour bien comprendre le code, vous devez comprendre la signification de ceci et de la fermeture .
Qu'est-ce que Promise
En termes simples, Promise vise principalement à résoudre le problème des rappels asynchrones. L'utilisation de Promise pour gérer les rappels asynchrones rend la hiérarchie du code claire, facile à comprendre et à maintenir. Ses spécifications principales sont actuellement principalement Promesses/A+. Si vous n'êtes pas familier avec l'utilisation de Promise, vous pouvez vous référer à mon article – notes d'étude es6 5 – promesse. Si vous le comprenez, alors lisez cet article, il vous sera d'une grande aide.
Avant de commencer, écrivons un scénario d’application de promesse pour découvrir le rôle des promesses. Google et Firefox prennent actuellement en charge les promesses es6. Nous utilisons setTimeout pour simuler un fonctionnement asynchrone. Le code spécifique est le suivant :
function fn1(resolve, reject) { setTimeout(function() { console.log('步骤一:执行'); resolve('1'); },500); } function fn2(resolve, reject) { setTimeout(function() { console.log('步骤二:执行'); resolve('2'); },100); } new Promise(fn1).then(function(val){ console.log(val); return new Promise(fn2); }).then(function(val){ console.log(val); return 33; }).then(function(val){ console.log(val); });
En fin de compte, la promesse que nous avons écrite peut également réaliser cette fonction.
Construction initiale
Écrivons une simple promesse. Le paramètre de Promise est la fonction fn. Passez la méthode de résolution définie en interne comme paramètre à fn et appelez fn. Lorsque l'opération asynchrone réussit, la méthode de résolution sera appelée, puis la fonction de rappel enregistrée dans then sera exécutée.
function Promise(fn){ //需要一个成功时的回调 var callback; //一个实例的方法,用来注册异步事件 this.then = function(done){ callback = done; } function resolve(){ callback(); } fn(resolve); }
Ajouter la prise en charge du chaînage
Ajoutez le chaînage ci-dessous, et la méthode de rappel réussie doit être convertie en tableau avant de pouvoir être stockée. En même temps, nous ajoutons des paramètres à la méthode de résolution afin qu'undéfini ne soit pas affiché.
function Promise(fn) { var promise = this, value = null; promise._resolves = []; this.then = function (onFulfilled) { promise._resolves.push(onFulfilled); return this; }; function resolve(value) { promise._resolves.forEach(function (callback) { callback(value); }); } fn(resolve); }
promesse = ceci, nous n'avons donc pas à nous soucier du changement soudain de ce pointeur à un moment donné.
Appelez la méthode then et placez le rappel dans la file d'attente promise._resolves
<🎜 ; >Lors de la création de l'objet Promise, appelez son fn et transmettez la méthode de résolution Lorsque l'opération asynchrone de fn est exécutée avec succès, la résolution sera appelée, c'est-à-dire
<span style="font-family:times new roman,times; color:#000000">. promise._resolves<code><span style="color:#000000">promise._resolves</span>
rappel dans la colonne<code><span style="color:#000000">resolve 方法</span>
méthode de résolution reçoit un paramètre sont les résultats renvoyés par les opérations asynchrones, ce qui facilite la transmission des valeurs.Le retour this dans la méthode then implémente les appels en chaîne.
Cependant, il y a encore quelques problèmes avec la promesse actuelle. Si je transmets une fonction qui ne contient pas d'opérations asynchrones, la résolution sera exécutée avant cette date, c'est-à-dire pour. disons que promise._resolves est un tableau vide.
resolve
Afin de résoudre ce problème, nous pouvons ajouter setTimeout dans la résolution pour placer la logique d'exécution du rappel dans à la fin de la file d'attente des tâches JS.
function resolve(value) { setTimeout(function() { promise._resolves.forEach(function (callback) { callback(value); }); },0); }
<p style="margin-bottom: 7px;">function Promise(fn) {<br/> var promise = this,<br/> value = null;<br/> promise._resolves = [];<br/> promise._status = 'PENDING';<br/><br/> this.then = function (onFulfilled) {<br/> if (promise._status === 'PENDING') {<br/> promise._resolves.push(onFulfilled);<br/> return this;<br/> }<br/> onFulfilled(value);<br/> return this;<br/> };<br/> function resolve(value) {<br/> setTimeout(function(){<br/> promise._status = "FULFILLED";<br/> promise._resolves.forEach(function (callback) {<br/> callback(value);<br/> })<br/> },0);<br/> }<br/><br/> fn(resolve);<br/>}<br/></p>
function resolve(value) { setTimeout(function(){ promise._status = "FULFILLED"; promise._resolves.forEach(function (callback) { value = callback(value); }) },0); }
this.then = function (onFulfilled) { return new Promise(function(resolve) { function handle(value) { var ret = isFunction(onFulfilled) && onFulfilled(value) || value; resolve(ret); } if (promise._status === 'PENDING') { promise._resolves.push(handle); } else if(promise._status === FULFILLED){ handle(value); } }) };
注意的是,new Promise() 中匿名函数中的 promise (promise._resolves中的 promise)指向的都是上一个 promise 对象, 而不是当前这个刚刚创建的。
首先我们返回的是新的一个promise对象,因为是同类型,所以链式仍然可以实现。
其次,我们添加了一个 handle 函数,handle 函数对上一个 promise 的 then 中回调进行了处理,并且调用了当前的 promise 中的 resolve 方法。
接着将 handle 函数添加到 上一个promise 的 promise._resolves 中,当异步操作成功后就会执行 handle 函数,这样就可以 执行 当前 promise 对象的回调方法。我们的目的就达到了。
有些人在这里可能会有点犯晕,有必要对执行过程分析一下,具体参看以下代码:
new Promise(fn1).then(fn2).then(fn3)})
fn1, fn2, fn3的函数具体可参看最前面的定义。
首先我们创建了一个 Promise 实例,这里叫做 promise1;接着会运行 fn1(resolve);
但是 fn1 中有一个 setTimeout 函数,于是就会先跳过这一部分,运行后面的第一个 then 方法;
then 返回一个新的对象 promise2, promise2 对象的 resolve方法和 then 方法的中回调函数 fn2 都被封装在 handle 中, 然后 handle 被添加到 promise1._resolves 数组中。
接着运行第二个 then 方法,同样返回一个新的对象 promise3, 包含 promise3 的 resolve 方法和回调函数 fn3 的 handle 方法被添加到 promise2._resolves 数组中。
到此两个 then 运行结束。 setTimeout 中的延迟时间一到,就会调用 promise1的 resolve方法。
resolve 方法的执行,会调用 promise1._resolves 数组中的回调,之前我们添加的 handle 方法就会被执行; 也就是 fn2 和 promsie2 的 resolve 方法,都被调用了。
以此类推,fn3 会和promise3 的 resolve 方法 一起执行,因为后面没有 then 方法了,promise3._resolves 数组是空的 。
至此所有回调执行结束
但这里还存在一个问题,就是我们的 then 里面函数不能对 Promise 对象进行处理。这里我们需要再次对 then 进行修改,使其能够处理 promise 对象。
this.then = function (onFulfilled) { return new Promise(function(resolve) { function handle(value) { var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; if( ret && typeof ret ['then'] == 'function'){ ret.then(function(value){ resolve(value); }); } else { resolve(ret); } } if (promise._status === 'PENDING') { promise._resolves.push(handle); } else if(promise._status === FULFILLED){ handle(value); } }) };
在 then 方法里面,我们对 ret 进行了判断,如果是一个 promise 对象,就会调用其 then 方法,形成一个嵌套,直到其不是promise对象为止。同时 在 then 方法中我们添加了调用 resolve 方法,这样链式得以维持。
失败处理
异步操作不可能都成功,在异步操作失败时,标记其状态为 rejected,并执行注册的失败回调。
有了之前处理 fulfilled 状态的经验,支持错误处理变得很容易。毫无疑问的是,在注册回调、处理状态变更上都要加入新的逻辑:
this.then = function (onFulfilled, onRejected) { return new Promise(function(resolve, reject) { function handle(value) { ....... } function errback(reason){ reason = isFunction(onRejected) && onRejected(reason) || reason; reject(reason); } if (promise._status === 'PENDING') { promise._resolves.push(handle); promise._rejects.push(errback); } else if(promise._status === 'FULFILLED'){ handle(value); } else if(promise._status === 'REJECTED') { errback(promise._reason); } }) }; function reject(value) { setTimeout(function(){ promise._status = "REJECTED"; promise._rejects.forEach(function (callback) { promise._reason = callback( value); }) },0); }
添加Promise.all方法
Promise.all 可以接收一个元素为 Promise 对象的数组作为参数,当这个数组里面所有的 Promise 对象都变为 resolve 时,该方法才会返回。
具体代码如下:
Promise.all = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } // 返回一个promise 实例 return new Promise(function(resolve,reject){ var i = 0, result = [], len = promises.length, count = len; // 每一个 promise 执行成功后,就会调用一次 resolve 函数 function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ // 存储每一个promise的参数 result[index] = value; // 等于0 表明所有的promise 都已经运行完成,执行resolve函数 if( --count == 0){ resolve(result) } } // 依次循环执行每个promise for (; i < len; i++) { // 若有一个失败,就执行rejecter函数 promises[i].then(resolver(i),rejecter); } }); }
Promise.all会返回一个 Promise 实例,该实例直到参数中的所有的 promise 都执行成功,才会执行成功回调,一个失败就会执行失败回调。
日常开发中经常会遇到这样的需求,在不同的接口请求数据然后拼合成自己所需的数据,通常这些接口之间没有关联(例如不需要前一个接口的数据作为后一个接口的参数),这个时候 Promise.all 方法就可以派上用场了。
添加Promise.race方法
该函数和 Promise.all 相类似,它同样接收一个数组,不同的是只要该数组中的任意一个 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。我们只需要对 Promise.all 方法稍加修改就可以了。
Promise.race = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return Promise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); }
代码中没有类似一个 resolveAll 的函数,因为我们不需要等待所有的 promise 对象状态都发生变化,只要一个就可以了。
添加其他API以及封装函数
到这里,Promise 的主要API都已经完成了,另外我们在添加一些比较常见的方法。也对一些可能出现的错误进行了处理,最后对其进行封装。
完整的代码如下:
(function(window,undefined){ // resolve 和 reject 最终都会调用该函数 var final = function(status,value){ var promise = this, fn, st; if(promise._status !== 'PENDING') return; // 所以的执行都是异步调用,保证then是先执行的 setTimeout(function(){ promise._status = status; st = promise._status === 'FULFILLED' queue = promise[st ? '_resolves' : '_rejects']; while(fn = queue.shift()) { value = fn.call(promise, value) || value; } promise[st ? '_value' : '_reason'] = value; promise['_resolves'] = promise['_rejects'] = undefined; }); } //参数是一个函数,内部提供两个函数作为该函数的参数,分别是resolve 和 reject var Promise = function(resolver){ if (!(typeof resolver === 'function' )) throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); //如果不是promise实例,就new一个 if(!(this instanceof Promise)) return new Promise(resolver); var promise = this; promise._value; promise._reason; promise._status = 'PENDING'; //存储状态 promise._resolves = []; promise._rejects = []; // var resolve = function(value) { //由於apply參數是數組 final.apply(promise,['FULFILLED'].concat([value])); } var reject = function(reason){ final.apply(promise,['REJECTED'].concat([reason])); } resolver(resolve,reject); } Promise.prototype.then = function(onFulfilled,onRejected){ var promise = this; // 每次返回一个promise,保证是可thenable的 return new Promise(function(resolve,reject){ function handle(value) { // 這一步很關鍵,只有這樣才可以將值傳遞給下一個resolve var ret = typeof onFulfilled === 'function' && onFulfilled(value) || value; //判断是不是promise 对象 if (ret && typeof ret ['then'] == 'function') { ret.then(function(value) { resolve(value); }, function(reason) { reject(reason); }); } else { resolve(ret); } } function errback(reason){ reason = typeof onRejected === 'function' && onRejected(reason) || reason; reject(reason); } if(promise._status === 'PENDING'){ promise._resolves.push(handle); promise._rejects.push(errback); }else if(promise._status === FULFILLED){ // 状态改变后的then操作,立刻执行 callback(promise._value); }else if(promise._status === REJECTED){ errback(promise._reason); } }); } Promise.prototype.catch = function(onRejected){ return this.then(undefined, onRejected) } Promise.prototype.delay = function(ms,value){ return this.then(function(ori){ return Promise.delay(ms,value || ori); }) } Promise.delay = function(ms,value){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve(value); console.log('1'); },ms); }) } Promise.resolve = function(arg){ return new Promise(function(resolve,reject){ resolve(arg) }) } Promise.reject = function(arg){ return Promise(function(resolve,reject){ reject(arg) }) } Promise.all = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to all.'); } return Promise(function(resolve,reject){ var i = 0, result = [], len = promises.length, count = len //这里与race中的函数相比,多了一层嵌套,要传入index function resolver(index) { return function(value) { resolveAll(index, value); }; } function rejecter(reason){ reject(reason); } function resolveAll(index,value){ result[index] = value; if( --count == 0){ resolve(result) } } for (; i < len; i++) { promises[i].then(resolver(i),rejecter); } }); } Promise.race = function(promises){ if (!Array.isArray(promises)) { throw new TypeError('You must pass an array to race.'); } return Promise(function(resolve,reject){ var i = 0, len = promises.length; function resolver(value) { resolve(value); } function rejecter(reason){ reject(reason); } for (; i < len; i++) { promises[i].then(resolver,rejecter); } }); } window.Promise = Promise; })(window);
代码写完了,总要写几个实例看看效果啊,具体看下面的测试代码:
var getData100 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve('100ms'); },1000); }); } var getData200 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ resolve('200ms'); },2000); }); } var getData300 = function(){ return new Promise(function(resolve,reject){ setTimeout(function(){ reject('reject'); },3000); }); } getData100().then(function(data){ console.log(data); // 100ms return getData200(); }).then(function(data){ console.log(data); // 200ms return getData300(); }).then(function(data){ console.log(data); }, function(data){ console.log(data); // 'reject' }); Promise.all([getData100(), getData200()]).then(function(data){ console.log(data); // [ "100ms", "200ms" ] }); Promise.race([getData100(), getData200(), getData300()]).then(function(data){ console.log(data); // 100ms }); Promise.resolve('resolve').then(function(data){ console.log(data); //'resolve' }) Promise.reject('reject函数').then(function(data){ console.log(data); }, function(data){ console.log(data); //'reject函数' })
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!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

WeChat est l'un des outils de chat traditionnels. Nous pouvons rencontrer de nouveaux amis, contacter d'anciens amis et entretenir l'amitié entre amis grâce à WeChat. Tout comme il n’existe pas de banquet qui ne se termine jamais, des désaccords surviendront inévitablement lorsque les gens s’entendront bien. Lorsqu'une personne affecte extrêmement votre humeur ou que vous constatez que vos points de vue sont incohérents lorsque vous vous entendez bien et que vous ne pouvez plus communiquer, nous devrons peut-être supprimer les amis WeChat. Comment supprimer des amis WeChat ? La première étape pour supprimer des amis WeChat : appuyez sur [Carnet d'adresses] sur l'interface principale de WeChat ; la deuxième étape : cliquez sur l'ami que vous souhaitez supprimer et entrez [Détails] ; la troisième étape : cliquez sur [...] en haut. coin droit ; Étape 4 : Cliquez sur [Supprimer] ci-dessous ; Étape 5 : Après avoir compris les invites de la page, cliquez sur [Supprimer le contact] ;

Tomato Novel est un logiciel de lecture de romans très populaire. Nous avons souvent de nouveaux romans et bandes dessinées à lire dans Tomato Novel. De nombreux amis souhaitent également gagner de l'argent de poche et éditer le contenu de leur roman. Je veux écrire dans du texte. Alors, comment pouvons-nous y écrire le roman ? Mes amis ne le savent pas, alors allons ensemble sur ce site. Prenons le temps de regarder une introduction à la façon d'écrire un roman. Partagez le didacticiel du roman Tomato sur la façon d'écrire un roman. 1. Ouvrez d'abord l'application de roman gratuite Tomato sur votre téléphone mobile et cliquez sur Personal Center - Writer Center 2. Accédez à la page Tomato Writer Assistant - cliquez sur Créer un nouveau livre. à la fin du roman.

Les cartes mères colorées jouissent d'une grande popularité et d'une part de marché élevée sur le marché intérieur chinois, mais certains utilisateurs de cartes mères colorées ne savent toujours pas comment accéder au BIOS pour les paramètres ? En réponse à cette situation, l'éditeur vous a spécialement proposé deux méthodes pour accéder au bios coloré de la carte mère. Venez l'essayer ! Méthode 1 : utilisez la touche de raccourci de démarrage du disque U pour accéder directement au système d'installation du disque U. La touche de raccourci de la carte mère Colorful pour démarrer le disque U en un seul clic est ESC ou F11. Tout d'abord, utilisez Black Shark Installation Master pour créer un Black. Disque de démarrage Shark U, puis allumez l'ordinateur lorsque vous voyez l'écran de démarrage, appuyez continuellement sur la touche ESC ou F11 du clavier pour accéder à une fenêtre de sélection de la séquence d'éléments de démarrage. Déplacez le curseur à l'endroit où "USB. " s'affiche, puis

Malheureusement, les gens suppriment souvent certains contacts accidentellement pour certaines raisons. WeChat est un logiciel social largement utilisé. Pour aider les utilisateurs à résoudre ce problème, cet article explique comment récupérer les contacts supprimés de manière simple. 1. Comprendre le mécanisme de suppression des contacts WeChat. Cela nous offre la possibilité de récupérer les contacts supprimés. Le mécanisme de suppression des contacts dans WeChat les supprime du carnet d'adresses, mais ne les supprime pas complètement. 2. Utilisez la fonction intégrée « Récupération du carnet de contacts » de WeChat. WeChat fournit une « Récupération du carnet de contacts » pour économiser du temps et de l'énergie. Les utilisateurs peuvent récupérer rapidement les contacts précédemment supprimés grâce à cette fonction. 3. Accédez à la page des paramètres WeChat et cliquez sur le coin inférieur droit, ouvrez l'application WeChat « Moi » et cliquez sur l'icône des paramètres dans le coin supérieur droit pour accéder à la page des paramètres.

La définition de la taille de la police est devenue une exigence de personnalisation importante à mesure que les téléphones mobiles deviennent un outil important dans la vie quotidienne des gens. Afin de répondre aux besoins des différents utilisateurs, cet article présentera comment améliorer l'expérience d'utilisation du téléphone mobile et ajuster la taille de la police du téléphone mobile grâce à des opérations simples. Pourquoi avez-vous besoin d'ajuster la taille de la police de votre téléphone mobile - L'ajustement de la taille de la police peut rendre le texte plus clair et plus facile à lire - Adapté aux besoins de lecture des utilisateurs d'âges différents - Pratique pour les utilisateurs malvoyants qui souhaitent utiliser la taille de la police fonction de configuration du système de téléphonie mobile - Comment accéder à l'interface des paramètres du système - Dans Rechercher et entrez l'option "Affichage" dans l'interface des paramètres - recherchez l'option "Taille de la police" et ajustez-la. application - téléchargez et installez une application prenant en charge l'ajustement de la taille de la police - ouvrez l'application et entrez dans l'interface des paramètres appropriée - en fonction de l'individu

Un résumé de la façon d'obtenir les droits d'administrateur Win11 Dans le système d'exploitation Windows 11, les droits d'administrateur sont l'une des autorisations très importantes qui permettent aux utilisateurs d'effectuer diverses opérations sur le système. Parfois, nous pouvons avoir besoin d'obtenir des droits d'administrateur pour effectuer certaines opérations, telles que l'installation de logiciels, la modification des paramètres du système, etc. Ce qui suit résume quelques méthodes pour obtenir les droits d'administrateur Win11, j'espère que cela pourra vous aider. 1. Utilisez les touches de raccourci. Dans le système Windows 11, vous pouvez ouvrir rapidement l'invite de commande via les touches de raccourci.

Les jeux mobiles font désormais partie intégrante de la vie des gens avec le développement de la technologie. Il a attiré l'attention de nombreux joueurs avec sa jolie image d'œuf de dragon et son processus d'éclosion intéressant, et l'un des jeux qui a beaucoup attiré l'attention est la version mobile de Dragon Egg. Pour aider les joueurs à mieux cultiver et faire grandir leurs propres dragons dans le jeu, cet article vous présentera comment faire éclore des œufs de dragon dans la version mobile. 1. Choisissez le type d'œuf de dragon approprié. Les joueurs doivent choisir soigneusement le type d'œuf de dragon qu'ils aiment et qui leur conviennent, en fonction des différents types d'attributs et de capacités d'œuf de dragon fournis dans le jeu. 2. Améliorez le niveau de la machine d'incubation. Les joueurs doivent améliorer le niveau de la machine d'incubation en accomplissant des tâches et en collectant des accessoires. Le niveau de la machine d'incubation détermine la vitesse d'éclosion et le taux de réussite de l'éclosion. 3. Collectez les ressources nécessaires à l'éclosion. Les joueurs doivent être dans le jeu.

Explication détaillée de la méthode de requête de version Oracle Oracle est l'un des systèmes de gestion de bases de données relationnelles les plus populaires au monde. Il offre des fonctions riches et des performances puissantes et est largement utilisé dans les entreprises. Dans le processus de gestion et de développement de bases de données, il est très important de comprendre la version de la base de données Oracle. Cet article présentera en détail comment interroger les informations de version de la base de données Oracle et donnera des exemples de code spécifiques. Interrogez la version de base de données de l'instruction SQL dans la base de données Oracle en exécutant une simple instruction SQL
