cari
Rumahhujung hadapan webtutorial jsPenggunaan Promise secara menyeluruh dalam pengaturcaraan JavaScript_Pengetahuan asas

Walaupun Promise sudah mempunyai spesifikasinya sendiri, pelbagai perpustakaan Promise semasa mempunyai perbezaan dalam butiran pelaksanaan Promise, malah sesetengah API berbeza sama sekali dalam maksud. Tetapi kandungan teras Janji adalah sama, ia adalah kaedah kemudian. Dalam istilah yang berkaitan, janji merujuk kepada objek atau fungsi yang mempunyai kaedah kemudian yang boleh mencetuskan tingkah laku tertentu.

Promise boleh dilaksanakan dengan cara yang berbeza, jadi perihalan teras Promise tidak membincangkan sebarang kod pelaksanaan khusus.

Baca huraian inti Janji dahulu Maksudnya: Lihat, ini adalah hasil yang perlu ditulis. Sila rujuk hasil ini untuk memikirkan cara menulisnya dalam kod.

Mula: Fahami Janji dengan cara ini

Imbau kembali apakah masalah yang diselesaikan oleh Janji? panggil balik. Sebagai contoh, fungsi doMission1() mewakili perkara pertama Sekarang, kita mahu melakukan perkara seterusnya doMission2() selepas perkara ini selesai.

Mari kita lihat corak panggil balik biasa kami dahulu. doMission1() berkata: "Jika anda mahu melakukan ini, berikan saya doMission2() dan saya akan memanggilnya untuk anda selepas ia selesai."

Salin kod Kod adalah seperti berikut:
doMission1(doMission2);
Bagaimana pula dengan mod Janji? Anda berkata kepada doMission1(): "Tidak, kawalan adalah dengan saya. Anda harus mengubahnya. Anda mengembalikan sesuatu yang istimewa kepada saya dahulu, dan kemudian saya akan menggunakan perkara ini untuk mengatur perkara yang istimewa ini ialah Janji." ini akan kelihatan seperti ini:

Salin kod Kod adalah seperti berikut:doMission1().then(doMission2);
Dapat dilihat bahawa Promise mengubah hubungan tuan-hamba mod panggil balik (terbalik dan menjadi tuan!). fungsi).
Baiklah, bagaimana untuk melakukan penukaran sedemikian? Mari kita mulakan dengan kes yang paling mudah, dengan mengandaikan bahawa kod doMission1() ialah:

Salin kod Kod adalah seperti berikut:fungsi doMission1(panggilan balik){ nilai var = 1; panggil balik(nilai);
}


Kemudian, ia boleh diubah menjadi seperti ini:

Salin kod

Kod adalah seperti berikut:fungsi doMission1(){ kembali { Kemudian: fungsi(panggilan balik){
nilai var = 1;        panggil balik(nilai); 
}  
};
}


Ini melengkapkan penukaran. Walaupun ia bukan penukaran yang sebenarnya berguna, di sini kita sebenarnya telah menyentuh titik pelaksanaan Promise yang paling penting, iaitu, Promise menukar nilai pulangan kepada objek dengan kaedah kemudian.

Lanjutan: Perjalanan reka bentuk Q

Mulakan dari penangguhan

design/q0.js ialah langkah pertama dalam pembentukan awal Q. Ia mencipta fungsi utiliti yang dipanggil defer untuk mencipta Promise:

Seperti yang dapat dilihat daripada kod sumber ini, menjalankan defer() akan mendapat objek, yang mengandungi dua kaedah: menyelesaikan dan kemudian. Sila ingat jQuery's Deferred (juga selesaikan dan kemudian), kedua-dua kaedah ini akan mempunyai kesan yang sama. Kemudian akan merujuk kepada status belum selesai, dan jika ia berada dalam keadaan menunggu, panggilan balik akan disimpan (tolak), jika tidak panggilan balik akan dipanggil serta-merta. menyelesaikan akan mengesahkan Janji, mengemas kini nilai dan menjalankan semua panggilan balik yang disimpan pada masa yang sama. Contoh penggunaan tangguh adalah seperti berikut:

var defer = function () { 
 var pending = [], value; 
 return { 
 resolve: function (_value) { 
  value = _value; 
  for (var i = 0, ii = pending.length; i < ii; i++) { 
  var callback = pending[i]; 
  callback(value); 
  } 
  pending = undefined; 
 }, 
 then: function (callback) { 
  if (pending) { 
  pending.push(callback); 
  } else { 
  callback(value); 
  } 
 } 
 } 
}; 

Salin kod

Kod adalah seperti berikut:var oneOneSecondLater = function () { var result = defer(); setTimeout(fungsi () { Result.resolve(1); }, 1000); Pulangan hasil; };


oneOneSecondLater().then(callback);

这里oneOneSecondLater()包含异步内容(setTimeout),但这里让它立即返回了一个defer()生成的对象,然后将对象的resolve方法放在异步结束的位置调用(并附带上值,或者说结果)。

到此,以上代码存在一个问题:resolve可以被执行多次。因此,resolve中应该加入对状态的判断,保证resolve只有一次有效。这就是Q下一步的design/q1.js(仅差异部分):

resolve: function (_value) { 
 if (pending) { 
 value = _value; 
 for (var i = 0, ii = pending.length; i < ii; i++) { 
  var callback = pending[i]; 
  callback(value); 
 } 
 pending = undefined; 
 } else { 
 throw new Error("A promise can only be resolved once."); 
 } 
} 

对第二次及更多的调用,可以这样抛出一个错误,也可以直接忽略掉。

分离defer和promise

在前面的实现中,defer生成的对象同时拥有then方法和resolve方法。按照定义,promise关心的是then方法,至于触发promise改变状态的resolve,是另一回事。所以,Q接下来将拥有then方法的promise,和拥有resolve的defer分离开来,各自独立使用。这样就好像划清了各自的职责,各自只留一定的权限,这会使代码逻辑更明晰,易于调整。请看design/q3.js:(q2在此跳过)

var isPromise = function (value) { 
 return value && typeof value.then === "function"; 
}; 
 
var defer = function () { 
 var pending = [], value; 
 return { 
 resolve: function (_value) { 
  if (pending) { 
  value = _value; 
  for (var i = 0, ii = pending.length; i < ii; i++) { 
   var callback = pending[i]; 
   callback(value); 
  } 
  pending = undefined; 
  } 
 }, 
 promise: { 
  then: function (callback) { 
  if (pending) { 
   pending.push(callback); 
  } else { 
   callback(value); 
  } 
  } 
 } 
 }; 
}; 

如果你仔细对比一下q1,你会发现区别很小。一方面,不再抛出错误(改为直接忽略第二次及更多的resolve),另一方面,将then方法移动到一个名为promise的对象内。到这里,运行defer()得到的对象(就称为defer吧),将拥有resolve方法,和一个promise属性指向另一个对象。这另一个对象就是仅有then方法的promise。这就完成了分离。

前面还有一个isPromise()函数,它通过是否有then方法来判断对象是否是promise(duck-typing的判断方法)。为了正确使用和处理分离开的promise,会像这样需要将promise和其他值区分开来。


实现promise的级联

接下来会是相当重要的一步。到前面到q3为止,所实现的promise都是不能级联的。但你所熟知的promise应该支持这样的语法:

复制代码 代码如下:
promise.then(step1).then(step2);

以上过程可以理解为,promise将可以创造新的promise,且取自旧的promise的值(前面代码中的value)。要实现then的级联,需要做到一些事情:

then方法必须返回promise。

这个返回的promise必须用传递给then方法的回调运行后的返回结果,来设置自己的值。

传递给then方法的回调,必须返回一个promise或值。

design/q4.js中,为了实现这一点,新增了一个工具函数ref:

复制代码 代码如下:
var ref = function (value) { 
  if (value && typeof value.then === "function") 
    return value; 
  return { 
    then: function (callback) { 
      return ref(callback(value)); 
    } 
  }; 
}; 

这是在着手处理与promise关联的value。这个工具函数将对任一个value值做一次包装,如果是一个promise,则什么也不做,如果不是promise,则将它包装成一个promise。注意这里有一个递归,它确保包装成的promise可以使用then方法级联。为了帮助理解它,下面是一个使用的例子:

复制代码 代码如下:
ref("step1").then(function(value){ 
  console.log(value); // "step1" 
  return 15; 
}).then(function(value){ 
  console.log(value); // 15 
});

你可以看到value是怎样传递的,promise级联需要做到的也是如此。

design/q4.js通过结合使用这个ref函数,将原来的defer转变为可级联的形式:

var defer = function () { 
 var pending = [], value; 
 return { 
 resolve: function (_value) { 
  if (pending) { 
  value = ref(_value); // values wrapped in a promise 
  for (var i = 0, ii = pending.length; i < ii; i++) { 
   var callback = pending[i]; 
   value.then(callback); // then called instead 
  } 
  pending = undefined; 
  } 
 }, 
 promise: { 
  then: function (_callback) { 
  var result = defer(); 
  // callback is wrapped so that its return 
  // value is captured and used to resolve the promise 
  // that "then" returns 
  var callback = function (value) { 
   result.resolve(_callback(value)); 
  }; 
  if (pending) { 
   pending.push(callback); 
  } else { 
   value.then(callback); 
  } 
  return result.promise; 
  } 
 } 
 }; 
}; 

原来callback(value)的形式,都修改为value.then(callback)。这个修改后效果其实和原来相同,只是因为value变成了promise包装的类型,会需要这样调用。

then方法有了较多变动,会先新生成一个defer,并在结尾处返回这个defer的promise。请注意,callback不再是直接取用传递给then的那个,而是在此基础之上增加一层,并把新生成的defer的resolve方法放置在此。此处可以理解为,then方法将返回一个新生成的promise,因此需要把promise的resolve也预留好,在旧的promise的resolve运行后,新的promise的resolve也会随之运行。这样才能像管道一样,让事件按照then连接的内容,一层一层传递下去。


加入错误处理

promise的then方法应该可以包含两个参数,分别是肯定和否定状态的处理函数(onFulfilled与onRejected)。前面我们实现的promise还只能转变为肯定状态,所以,接下来应该加入否定状态部分。

请注意,promise的then方法的两个参数,都是可选参数。design/q6.js(q5也跳过)加入了工具函数reject来帮助实现promise的否定状态:

var reject = function (reason) { 
 return { 
 then: function (callback, errback) { 
  return ref(errback(reason)); 
 } 
 }; 
}; 

它和ref的主要区别是,它返回的对象的then方法,只会取第二个参数的errback来运行。design/q6.js的其余部分是:

var defer = function () { 
 var pending = [], value; 
 return { 
 resolve: function (_value) { 
  if (pending) { 
  value = ref(_value); 
  for (var i = 0, ii = pending.length; i < ii; i++) { 
   value.then.apply(value, pending[i]); 
  } 
  pending = undefined; 
  } 
 }, 
 promise: { 
  then: function (_callback, _errback) { 
  var result = defer(); 
  // provide default callbacks and errbacks 
  _callback = _callback || function (value) { 
   // by default, forward fulfillment 
   return value; 
  }; 
  _errback = _errback || function (reason) { 
   // by default, forward rejection 
   return reject(reason); 
  }; 
  var callback = function (value) { 
   result.resolve(_callback(value)); 
  }; 
  var errback = function (reason) { 
   result.resolve(_errback(reason)); 
  }; 
  if (pending) { 
   pending.push([callback, errback]); 
  } else { 
   value.then(callback, errback); 
  } 
  return result.promise; 
  } 
 } 
 }; 
}; 

这里的主要改动是,将数组pending只保存单个回调的形式,改为同时保存肯定和否定的两种回调的形式。而且,在then中定义了默认的肯定和否定回调,使得then方法满足了promise的2个可选参数的要求。

你也许注意到defer中还是只有一个resolve方法,而没有类似jQuery的reject。那么,错误处理要如何触发呢?请看这个例子:

var defer1 = defer(), 
promise1 = defer1.promise; 
promise1.then(function(value){ 
 console.log("1: value = ", value); 
 return reject("error happens"); 
}).then(function(value){ 
 console.log("2: value = ", value); 
}).then(null, function(reason){ 
 console.log("3: reason = ", reason); 
}); 
defer1.resolve(10); 
 
// Result: 
// 1: value = 10 
// 3: reason = error happens 

可以看出,每一个传递给then方法的返回值是很重要的,它将决定下一个then方法的调用结果。而如果像上面这样返回工具函数reject生成的对象,就会触发错误处理。


融入异步

终于到了最后的design/q7.js。直到前面的q6,还存在一个问题,就是then方法运行的时候,可能是同步的,也可能是异步的,这取决于传递给then的函数(例如直接返回一个值,就是同步,返回一个其他的promise,就可以是异步)。这种不确定性可能带来潜在的问题。因此,Q的后面这一步,是确保将所有then转变为异步。

design/q7.js定义了另一个工具函数enqueue:

复制代码 代码如下:
var enqueue = function (callback) { 
  //process.nextTick(callback); // NodeJS 
  setTimeout(callback, 1); // Na?ve browser solution 
};


显然,这个工具函数会将任意函数推迟到下一个事件队列运行。

design/q7.js其他的修改点是(只显示修改部分):

var ref = function (value) { 
 // ... 
 return { 
 then: function (callback) { 
  var result = defer(); 
  // XXX 
  enqueue(function () { 
  result.resolve(callback(value)); 
  }); 
  return result.promise; 
 } 
 }; 
}; 
 
var reject = function (reason) { 
 return { 
 then: function (callback, errback) { 
  var result = defer(); 
  // XXX 
  enqueue(function () { 
  result.resolve(errback(reason)); 
  }); 
  return result.promise; 
 } 
 }; 
}; 
 
var defer = function () { 
 var pending = [], value; 
 return { 
 resolve: function (_value) { 
  // ... 
   enqueue(function () { 
   value.then.apply(value, pending[i]); 
   }); 
  // ... 
 }, 
 promise: { 
  then: function (_callback, _errback) { 
   // ... 
   enqueue(function () { 
   value.then(callback, errback); 
   }); 
   // ... 
  } 
 } 
 }; 
}; 

即把原来的value.then的部分,都转变为异步。

到此,Q提供的Promise设计原理q0~q7,全部结束。


结语

即便本文已经是这么长的篇幅,但所讲述的也只到基础的Promise。大部分Promise库会有更多的API来应对更多和Promise有关的需求,例如all()、spread(),不过,读到这里,你已经了解了实现Promise的核心理念,这一定对你今后应用Promise有所帮助。

在我看来,Promise是精巧的设计,我花了相当一些时间才差不多理解它。Q作为一个典型Promise库,在思路上走得很明确。可以感受到,再复杂的库也是先从基本的要点开始的,如果我们自己要做类似的事,也应该保持这样的心态一点一点进步。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Python vs JavaScript: Bahasa mana yang harus anda pelajari?Python vs JavaScript: Bahasa mana yang harus anda pelajari?May 03, 2025 am 12:10 AM

Memilih Python atau JavaScript harus berdasarkan perkembangan kerjaya, keluk pembelajaran dan ekosistem: 1) Pembangunan Kerjaya: Python sesuai untuk sains data dan pembangunan back-end, sementara JavaScript sesuai untuk pembangunan depan dan penuh. 2) Kurva Pembelajaran: Sintaks Python adalah ringkas dan sesuai untuk pemula; Sintaks JavaScript adalah fleksibel. 3) Ekosistem: Python mempunyai perpustakaan pengkomputeran saintifik yang kaya, dan JavaScript mempunyai rangka kerja front-end yang kuat.

Rangka Kerja JavaScript: Menguasai Pembangunan Web ModenRangka Kerja JavaScript: Menguasai Pembangunan Web ModenMay 02, 2025 am 12:04 AM

Kuasa rangka kerja JavaScript terletak pada pembangunan yang memudahkan, meningkatkan pengalaman pengguna dan prestasi aplikasi. Apabila memilih rangka kerja, pertimbangkan: 1.

Hubungan antara JavaScript, C, dan penyemak imbasHubungan antara JavaScript, C, dan penyemak imbasMay 01, 2025 am 12:06 AM

Pengenalan Saya tahu anda mungkin merasa pelik, apa sebenarnya yang perlu dilakukan oleh JavaScript, C dan penyemak imbas? Mereka seolah -olah tidak berkaitan, tetapi sebenarnya, mereka memainkan peranan yang sangat penting dalam pembangunan web moden. Hari ini kita akan membincangkan hubungan rapat antara ketiga -tiga ini. Melalui artikel ini, anda akan mempelajari bagaimana JavaScript berjalan dalam penyemak imbas, peranan C dalam enjin pelayar, dan bagaimana mereka bekerjasama untuk memacu rendering dan interaksi laman web. Kita semua tahu hubungan antara JavaScript dan penyemak imbas. JavaScript adalah bahasa utama pembangunan front-end. Ia berjalan secara langsung di penyemak imbas, menjadikan laman web jelas dan menarik. Adakah anda pernah tertanya -tanya mengapa Javascr

Aliran node.js dengan typescriptAliran node.js dengan typescriptApr 30, 2025 am 08:22 AM

Node.js cemerlang pada I/O yang cekap, sebahagian besarnya terima kasih kepada aliran. Aliran memproses data secara berperingkat, mengelakkan beban memori-ideal untuk fail besar, tugas rangkaian, dan aplikasi masa nyata. Menggabungkan sungai dengan keselamatan jenis typescript mencipta powe

Python vs JavaScript: Pertimbangan Prestasi dan KecekapanPython vs JavaScript: Pertimbangan Prestasi dan KecekapanApr 30, 2025 am 12:08 AM

Perbezaan prestasi dan kecekapan antara Python dan JavaScript terutamanya dicerminkan dalam: 1) sebagai bahasa yang ditafsirkan, Python berjalan perlahan tetapi mempunyai kecekapan pembangunan yang tinggi dan sesuai untuk pembangunan prototaip pesat; 2) JavaScript adalah terhad kepada benang tunggal dalam penyemak imbas, tetapi I/O multi-threading dan asynchronous boleh digunakan untuk meningkatkan prestasi dalam node.js, dan kedua-duanya mempunyai kelebihan dalam projek sebenar.

Asal JavaScript: Meneroka Bahasa PelaksanaannyaAsal JavaScript: Meneroka Bahasa PelaksanaannyaApr 29, 2025 am 12:51 AM

JavaScript berasal pada tahun 1995 dan dicipta oleh Brandon Ike, dan menyedari bahasa itu menjadi C. 1.C Language menyediakan keupayaan pengaturcaraan prestasi tinggi dan sistem untuk JavaScript. 2. Pengurusan memori JavaScript dan pengoptimuman prestasi bergantung pada bahasa C. 3. Ciri lintas platform bahasa C membantu JavaScript berjalan dengan cekap pada sistem operasi yang berbeza.

Di sebalik tabir: Apa bahasa JavaScript?Di sebalik tabir: Apa bahasa JavaScript?Apr 28, 2025 am 12:01 AM

JavaScript berjalan dalam penyemak imbas dan persekitaran Node.js dan bergantung pada enjin JavaScript untuk menghuraikan dan melaksanakan kod. 1) menjana pokok sintaks abstrak (AST) di peringkat parsing; 2) menukar AST ke bytecode atau kod mesin dalam peringkat penyusunan; 3) Laksanakan kod yang disusun dalam peringkat pelaksanaan.

Masa Depan Python dan JavaScript: Trend dan RamalanMasa Depan Python dan JavaScript: Trend dan RamalanApr 27, 2025 am 12:21 AM

Trend masa depan Python dan JavaScript termasuk: 1. Kedua -duanya akan terus mengembangkan senario aplikasi dalam bidang masing -masing dan membuat lebih banyak penemuan dalam prestasi.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

SublimeText3 versi Inggeris

SublimeText3 versi Inggeris

Disyorkan: Versi Win, menyokong gesaan kod!

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

SublimeText3 Linux versi baharu

SublimeText3 Linux versi baharu

SublimeText3 Linux versi terkini

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

Persekitaran pembangunan bersepadu PHP yang berkuasa