

*花盆是由小盆慢慢種到大盆,這種過程比較不會對玫瑰產生壓力。
*疏鬆營養通風的土壤,最適合玫瑰生長
粗泥炭土、碳化稻殼、椰塊、火山石、蚯蚓糞土、中大粒赤玉土
*需要通風良好的土當介質。
*澆水:需要土乾後再澆水,每次要把土交透後才行。
*植株的間隔要有空間讓空氣流通,病菌與害蟲不容易互相傳染。
*玫瑰基本的日照為,日光直射4的小時。
*用小蘇打粉加水,比例為1:1000。噴灑的時間為。頻率是每週一次。
用途是消毒。
*葵無露:沙拉油9、洗碗精1的比例混合後,以10:1000的比例噴灑。頻率也是每週一次。
用途是殺菌和殺蟲。
玫瑰花有愈修剪愈開花的特性,所以,修剪對它愈好(應該說是人類吧)。
下肥的時機:
*春天:下液肥、花開之後下禮肥。
*夏天:下液肥,容易吸收。摘下花苞讓植物休息,不要開花。
*冬天:放固體肥。在花盆外圍挖三個洞,放入肥料讓植物吸收。
肥料種類:
氮肥--促進葉子的生長。
磷肥--幫助植物開花。
鉀肥--促進植物健壯莖及生根,並使植物結果。
咖啡渣:氮肥,必須要充分發酵後才可以。
蛋殼:鈣、磷。幫助植物開花結果。
香蕉皮:鉀肥。
香蕉皮液肥的做法:
一、剩下的香蕉皮剪成小片,放入容器中加水,加到八分滿。
二、放置於半陰涼的地方,每天一次打開瓶蓋並搖晃。
三、快的話3-4天就可以,放置兩個月也行。
澆水比例:
1:10,要淡一點也可以,以薄肥多澆的原則最好。
美國總統川普(Donald Trump)2日宣布大規模徵收「對等關稅」計畫,美國各地海關由台灣時間9日中午12時起,開始對全球86國輸美貨品課徵新關稅,範圍遍及世界各角落。業者過去把生產基地由中國移至東南亞和墨西哥,遷廠與洗產地等策略這次將難以複製,工廠恐無法轉進第三地規避進口關稅,20世紀中起建立的國際經貿秩序將重新洗牌。
4月2日川普公布加關稅細節,臺灣排名第四名,加34%對等關稅。公布時正值臺灣春假期間,星期一(7)股市開市後就………
台股今(7)日爆出股災,受到國際利空與連假累積賣壓衝擊,加權指數終場狂瀉2065.87點,收在19232.35點,跌幅高達9.7%;許多個股開盤即鎖跌停價,導致成交量急縮,成交量1473億元。權值股台積電、聯發科、鴻海等全數跌停,高價股創意、健策失守千金。
玫瑰葉子黃了怎麼辦?應該有很多花友會碰到這個問題~~
*玫瑰葉子黃了怎麼辦?應該有很多花友會碰到這個問題~~
其實這多半是因爲、換了新環境、澆水不當、施肥不當、光照不當、溫度不適合、有患病蟲害~引起的,一起來看看究竟怎麼辦吧!
一、玫瑰葉子黃了的原因
1、澆水不當
玫瑰花是一種非常耐旱的植株,並不能澆水過量。玫瑰花在養殖過程中要是澆水過多,就會導致土壤積水,進一步就會導致玫瑰花的根部腐爛,養分輸送不到植株枝葉,葉子自然就會枯黃。但是長時間不澆水,也會令玫瑰花澆水缺水,導致葉子變黃。
解決方法:要是澆水過少,乾旱原因導致的葉子發黃,只要少量補水就行。澆水過多就要將植株脫盆,看看玫瑰花根部有沒有腐爛,剪掉腐爛部位,泡一泡殺菌劑消毒,之後換上乾燥的土壤再重新種植,後期減少澆水量。
2、光照不當
玫瑰需要適量的陽光才能保證植株的生長,平時光照過少,就會讓玫瑰花葉子缺少葉綠素,就會很快變黃和掉落。但是要是在夏季的時候光照過多,又會因爲被陽光曬的葉緣發枯,葉片部分也會出現黃斑。
解決方法:很多人不知道光照導致的玫瑰葉子黃了怎麼辦,其實光照過少的玫瑰就應該將其搬到陽光下面,曬太陽,玫瑰光合反應之後就會慢慢綠葉。光照過多的情況就要移放到陰涼環境,然後剪掉已經變黃的葉子,適量補水養護。
3、溫度不當
玫瑰花喜歡生長在溫暖的環境中,冬天氣溫太低的時候,玫瑰會進入休眠期,不少葉子就會變黃,進而掉落。但是玫瑰花在夏季太炎熱的時候,也會因爲溫度太高導致蒸騰作用,使得植株葉子脫水,慢慢捲曲變黃。
解決方法:玫瑰花凍得葉子發黃的話,最好將植株移放到溫暖的陽臺上,一邊曬太陽,一邊保溫,將溫度保持在8℃以上。夏天氣溫太高導致的葉子發黃就要立刻降溫處理,將其移放到陰涼環境,然後向植株周圍噴水霧保溼降溫。
4、施肥不當
玫瑰施肥不當的時候也會在造成葉子變得枯黃的。施肥太勤、太多會出現玫瑰的老葉尖變黃、枯落。但是因爲長時間沒有肥料的養護造成黃葉,是會出現新葉發黃,葉色變淺,最後慢慢的掉落。
解決方法:玫瑰是少肥造成的黃葉就要及時添加肥料,但是肥料過多的話,就要儘量多澆水來稀釋過濃的肥料。但是要是澆水之後不起效的話,就要換掉肥料過多的土壤,換上一盆疏鬆透氣的土壤,重新種植玫瑰。
二、預防玫瑰葉子黃的方法
1、環境保溼
想要預防玫瑰花出現黃葉現象的話,最重要的是做到養殖玫瑰環境保溼。只有溼潤的環境纔有利於玫瑰植株的生長。春秋季節保持每天向玫瑰植株和周圍環境噴一次水霧保溼。夏天的時候早上和下午都要噴水霧保溼,冬天的時候就要減少噴水霧次數了。
2、修剪花葉
玫瑰花的生長過程中也會出現因花枝太濃密導致的植株缺少養分,這樣也是很容易黃葉的。所以在每年的春季的生長期的時候,要將植株裏面細弱,或是病枝枯枝剪掉,這樣纔不會因爲植株太過茂密導致缺少養分而黃葉。
3、土壤補鐵
想要讓玫瑰這種木本植株不出現黃葉現象的話,一定要時常注重它的土壤質量。木本植株的土壤是不能缺鐵的,在養殖的時候應該時常爲土壤補“鐵”,其實只要加入一點點的硫酸亞鐵水溶液就行了,或者是在施肥的時候用肥餅加上硫酸亞鐵,然後稀釋在水中之後施加在玫瑰的根部。
4、植株防病
玫瑰花要是患病的話,也是會有掉葉子黃葉子的現象的,注意玫瑰的生長環境不要太潮溼和悶熱,一定要保持通風纔不會患病。植株患病之後,就要用殺菌劑或常用藥噴灑植株,只要多噴兩次,就會讓玫瑰藥到病除了。
5.連續下雨的潮濕天氣、風大的天氣都會使玫瑰黃葉、這時就要先避免長時間淋雨、把玫瑰先移至屋簷下避風避雨、再噴一下殺菌劑消毒、就會改善!
值通過使用可選的返回語句返回。可以返回包括數組和對象的任意類型。返回語句會立即中止函數的運行,並且將控製權交回調用該函數的代碼行。
<?php
function square($num)
{
return $num * $num;
}
echo square(4); // 输出 '16'。
?>
函數不能返回多個值,但可以通過返回一個數組來得到類似的效果。
<?php
function small_numbers()
{
return [0, 1, 2];
}
// 使用短数组语法将数组中的值赋给一组变量
[$zero, $one, $two] = small_numbers();
// 在 7.1.0 之前,唯一相等的选择是使用 list() 结构
list($zero, $one, $two) = small_numbers();
?>
(PHP 5 >= 5.3.0, PHP 7, PHP 8)
連結串列是在許多程式語言中實現的常見資料結構。它是線性的,包含相互連結的節點。
每個節點都包含資料和到相鄰節點的連結。因此,連結串列形成了一個節點鏈。連結串列有不同的變體。
1.單連結串列:單向。它只在向前的方向上移動。
2.雙連結串列:是雙向的。它向前和向後兩個方向遍歷。
3.迴圈連結串列:單向迴圈。
4.迴圈雙向連結串列:雙向迴圈。
我們可以在連結串列中進行各種操作。基本操作如下:
1.遍歷
2.插入
3.刪除
4.更新
5.搜尋
PHP 提供了一個類 SplDoublyLinkedList 用於實現連結串列。它是一個雙向連結串列。
$list = new SplDoublyLinkedList();
$list->push(10);
$list->push(15);
$list->push(20);
displayList($list);
function displayList($list) {
for ($list->rewind(); $list->valid(); $list->next()){
echo $list->current()."<br>";
}
}
push() 方法接受要推送的引數並允許在列表中附加值。該元素將被推到連結串列的末尾。
rewind() 方法從連結串列的開頭回退迭代器。例如,迭代器將移動到列表的第一個元素。
valid() 方法檢查連結串列是否包含更多節點,next() 方法移動到連結串列的下一項。因此,我們可以在 for 迴圈中使用上述示例中的這些方法來遍歷連結串列的元素。
在迴圈內部,current( )方法表示當前元素。因此,將列印當前元素。使用 pop() 方法刪除連結串列中的元素
使用 pop() 方法刪除連結串列中的元素
我們可以使用 pop() 方法從連結串列中刪除最後一個元素。該方法不接受任何引數。
從連結列表中查詢頂部和底部值
我們可以使用 top() 方法找到連結串列的頂部值,對於底部值,我們可以使用 bottom() 方法
echo "top_element=".$list->top()."<br>";
echo "bottom_element=".$list->bottom();
使用 add() 方法在連結串列中插入值
我們可以使用 add() 方法通過指定位置在連結串列中插入元素。該方法有兩個引數。
第一個引數是要插入專案的索引,第二個引數是要插入的專案。例如,以 2 和 50 為引數呼叫 add() 方法並呼叫 displayList() 方法。
$list->add(2,50);
displayList($list);
PHP link.php
注意插入點會從第n個參數下插入
堆疊結構在電腦中的應用相當廣泛,時常被用來解決電腦的問題,例如前面所談到的遞迴呼叫、副程式的呼叫,至於在日常生活中的應用也隨處可以看到,例如大樓電梯、貨架上的貨品等等,都是類似堆疊的資料結構原理。
佇列在電腦領域的應用也相當廣泛,例如計算機的模擬simulation、CPU的工作排程Job Scheduling、線上同時周邊作業系統的應用與圖形走訪得先廣後深搜尋法BFS。由於堆疊與佇列都是抽象資料型態Abstract Data Type,ADT,本章終將為各位介紹相關演算法。
堆疊在程式設計領域中,包含以下兩種設計方式,分別為陣列結構與鏈結串列結構。
以陣列結構來製作堆疊的好處是製作與設計的演算法都相當簡單,但因為如果堆疊本身是變動的話,大小並無法事先規劃宣告,太大時浪費空間,太小則不夠使用。
//判斷是否空堆疊
var isEmpy=()=>{
if (top==-1)
return true;
else
return false;
}
//將指定的資料存入堆疊
var push=(data)=>{
if (top>=MAXSTACK-1)
process.stdout.write('堆疊已滿,無法再加入');
else {
top +=1;
stack[top]=data; //將資料存入堆疊
}
}
//從堆疊取出資料
var pop=()=>{
if (isEmpty())
process.stdout.write('堆疊是空');
else {
process.stdout.write('彈出的元素為: '+stack[top]+'\n');
top=top-1;
}
}
JS array_stack.js
const MAXSTACK=100; //定義最大堆疊容量
stack=[]; //堆疊的陣列宣告
top=-1; //堆疊的頂端
//判斷是是否為空堆疊
var isEmpty=()=>{
if(top==-1)
return true;
else
return false;
}
//將指定的資料存入堆疊
var push=(data)=> {
if (top>=MAXSTACK-1)
process.stdout.write('堆疊已滿,無法再加入');
else {
top +=1;
stack[top]=data;//將資料存入堆疊
}
}
//從堆疊取出資料
var pop=()=> {
if (isEmpty())
process.stdout.write('堆疊是空');
else {
process.stdout.write('彈出的元素為:'+stack[top]+'\n');
top=top-1;
}
}
//主程式
i=2;
count=0;
const prompt = require('prompt-sync')();
while(true) {
const i = parseInt(prompt('要推入堆疊,請輸入1,彈出則輸入0,停止操作則輸入-1:'));
if (i==-1)
break;
else if (i==1) {
const value = parseInt(prompt('請輸入元素值:'));
push(value);
}
else if (i==0)
pop();
}
process.stdout.write('========================\n');
if (top<0)
process.stdout.write('\n 堆疊是空的\n');
else {
i=top;
while (i>=0) {
process.stdout.write('堆疊彈出的順序為:'+stack[i]+'\n');
count +=1;
i =i-1;
}
}
process.stdout.write('=====================================\n');
PHP array_stack.php
使用鏈結串列來製作堆疊的優點是隨時可以動態改變串列長度,能有效利用記憶體資源,不過缺點是設計時,演算法較為複雜。
相關演算法如下:
class Node { //堆疊鏈結點的宣告
constructor() {
this.data=0; //堆疊資料的宣告
this.next=null; //堆疊中用來指向下一個節點
}
}
top=null;
var isEmpty=()=> {
if(top===null)
return true;
else
return false;
}
//將指定的資料存入堆疊
var push=(data)=> {
new_add_node=new Node();
new_add_node.data=data; //將傳入的值指定為節點的內容
new_add_node.next=top; //將新節點指向堆疊的頂端
top=new_add_node; //新節點成為堆疊的頂端
}
//從堆疊取出資料
var pop=()=> {
if (isEmpty()) {
process.studout.write('===目前為空堆疊====\n');
return -1;
}
else {
ptr=top; //指向堆疊的頂端
top=top.next; //將堆疊頂端的指標指向下一個節點
temp=ptr.data; //取出堆疊的資料
return temp; //將從堆疊取出的資料回傳給主程式
}
}
在單向鏈結型態的資料結構中,如果要在串列中刪除一個節點,如同一列火車拿掉原有的車廂,依據所刪除節點的位置有三種不同的情形:
演算法如下:
top=head;
head=head.next;
演算法如下:
ptr.next=tail;
ptr.next=null;
演算法如下:
Y=ptr.next;
ptr.next=Y.next;
class employee{
constructor() {
this.num=0;
this.salary=0;
this.name=”;
this.next=null;
}
}
JS del_node.js
class employee{
constructor(){
this.num=0;
this.salary=0;
this.name='';
this.next=null;
}
}
var del_ptr=(head,ptr)=>{ //刪除節點副程式
top=head;
if (ptr.num==head.num) { //[情形1]:刪除點在串列首
head=head.next;
process.stdout.write('已刪除第' +ptr.num+' 號員工 姓名:'+ptr.
name+' 薪資:'+ptr.salary);
}
else {
while (top.next!=ptr) //找到刪除點的前一個位置
top=top.next;
if(ptr.next==null) { //刪除在串列尾的節點
top.next=null;
process.stdout.write('已刪除第'+ptr.num+' 號員工 姓名:'+ptr.
name+' 薪資:'+ptr.salary+'\n');
}
else{
top.next=ptr.next;
process.stdout.write('已刪除第 '+ptr.num+' 號員工 姓名:'+ptr.
name+' 薪資:' +ptr.salary+'\n');
}
}
return head;
}
findword=0;
namedata=['Allen','Scott','Mary','John','Mark','Ricky',
'Lisa','Jasica','Hanson','Daniel','Axel','Jack'];
data=[[1001,32367],[1002,24388],[1003,27556],[1007,31299],
[1012,42660],[1014,25676],[1018,44145],[1043,52182],
[1031,32769],[1037,21100],[1041,32196],[1046,25776]];
process.stdout.write('員工編號 薪水 員工編號 薪水 員工編號 薪水 員工編號 薪水\n');
process.stdout.write('--------------------------------------------------\n');
for(i=0; i<3; i++) {
for (j=0; j<4; j++)
process.stdout.write(data[j*3+i][0]+ '\t'+data[j*3+i][1]+'\t');
console.log();
}
head=new employee(); //建立串列首
if(!head) {
console.log('Error!! 記憶體配置失敗!!');
return;
}
head.num=data[0][0];
head.name=namedata[0];
head.salary=data[0][1];
head.next=null;
ptr=head;
for (i=1; i<12; i++) { //建立串列
newnode=new employee();
newnode.next=null;
newnode.num=data[i][0];
newnode.name=namedata[i];
newnode.salary=data[i][1];
newnode.next=null;
ptr.next=newnode;
ptr=ptr.next;
}
const prompt = require('prompt-sync')();
while(true) {
const findword = parseInt(prompt('請輸入要刪除的員工編號,要結束刪除過程,請輸入-1:'));
if (findword==-1) //迴圈中斷條件
break;
else {
ptr=head;
find=0;
while (ptr!=null) {
if (ptr.num==findword){
ptr=del_ptr(head,ptr);
find=find+1;
head=ptr;
}
ptr=ptr.next;
}
if (find==0)
process.stdout.write('//////////沒有找到///////////\n');
}
}
ptr=head;
process.stdout.write('\t座號\t 姓名\t成績\n'); //列印剩餘串列資料
process.stdout.write('\t======================\n');
while (ptr!=null) {
process.stdout.write('\t['+ptr.num+' ]\t[ '+ptr.name+' ]\t[ '
+ptr.salary+']\n');
ptr=ptr.next;
}
看完了節點的刪除及插入後,各位可以發現在這種具有方向性的鏈結串列結構中增刪節點是相當容易的一件事。而要從頭到尾印整個串列似乎也不難,不過如果要反轉過來列印就真的需要某些技巧了。我們知道在鏈結串列中的節點特性是知道下一個節點的位置,可是卻無從得知它的上一個節點位置,不過如果要將串列反轉,則必須使用三個指標變數。請看下圖說明:
演算法如下:
class employee{
constructor() {
this.num=0;
this.salary=0;
this.name='';
this.next=null;
}
}
var invert=(x)=> { //x為串列的開始指標
p=x; //將p指向串列的開頭
q=null; //q是p的前一個節點
while (p!=null) {
r=q; //將r接到q之後
q=p; //將q接到p之後
p=p.next //p移到下一個節點
q.next=r; //q連結到之前的節點
}
return q;
}
JS rev_node.js
class employee {
constructor() {
this.num=0;
this.salary=0;
this.name='';
this.next=null;
}
}
findword=0;
namedata=['Allen','Scott','Marry','John','Mark','Ricky',
'Lisa','Jasica','Hanson','Daniel','Axel','Jack'];
data=[[1001,32367],[1002,24388],[1003,27556],[1007,31299],
[1012,42660],[1014,25676],[1018,44145],[1043,52182],
[1031,32769],[1037,21100],[1041,32196],[1046,25776]];
head = new employee();//建立串列首
if(!head) {
console.log('Error!! 記憶體配置失敗!!');
return;
}
head.num =data[0][0];
head.name=namedata[0];
head.salary=data[0][1];
head.next=null;
ptr=head;
for(i=1; i<12; i++){ //建立串列
newnode=new employee();
newnode.next=null;
newnode.num=data[i][0];
newnode.name=namedata[i];
newnode.salary=data[i][1];
newnode.next=null;
ptr.next=newnode;
ptr=ptr.next;
}
ptr=head;
i=0;
process.stdout.write('原始員工串列節點資料:\n');
while (ptr !=null) { //列印串列資料
process.stdout.write('['+ptr.num+'\t'+ptr.name+'\t'
+ptr.salary+'] -> ');
i=i+1;
if (i>=3) { //三個元素為一列
console.log();
i=0;
}
ptr=ptr.next;
}
ptr=head;
before=null;
process.stdout.write('\n反轉後串列節點資料:\n');
while (ptr!=null) { //串列反轉,利用三指標
last=before;
before=ptr;
ptr=ptr.next;
before.next=last;
}
ptr=before;
while (ptr!=null) {
process.stdout.write('['+ptr.num+'\t'+ptr.name+'\t'
+ptr.salary+'] ->');
i=i+1;
if (i>=3) { //三個元素為一列
console.log();
i=0;
}
ptr=ptr.next;
}
array_pop() 刪除陣列最後一個元素。
array_push() 將一個或多個元素加入末端。
array_reverse() 以相反的順序返回陣列。
array_shift() 刪除首個元素。
array_slice() 刪除指定位置的元素,返回陣列。
在 PHP 中,佇列可以使用陣列來實作。一個佇列就是一個先進先出的資料集合。我們可以在佇列的頭部插入數據,同時從佇列的尾部取出數據。
PHP queue.php
class Queue{
protected $queue=[];
//從尾部加入陣列
public function enqueue($item){
array_push($this->queue, $item);
}
//從頭部取出陣列
public function dequeue(){
if (empty($this->queue)){
return flase;
}
return array_shift($this->queue);
}
//獲取陣列的長度
public function length (){
return count($this->queue);
}
//判斷是否為空值
public function isEmpty(){
return empty($this->queue);
}
}
$queue=new Queue();
$queue->enqueue('a');
$queue->enqueue('b');
$queue->enqueue('c');
$length = $queue->length();
echo $length."<br>";
echo $queue->dequeue()."<br>";
echo $queue->dequeue()."<br>";
echo $queue->dequeue()."<br>";
使用函數
array_push() 在尾端加入陣列
— array_shift () 删除陣列中首個元素,并返回被删除元素的值。
— count() 計算陣列數量
— empty() 陣列是否為空
Event Table 是與Event Queue 互相搭配的資料集合,它負責記錄在非同步目的達成後,有哪些函式或者事件要被執行,這裡指的非同步目的指的是像計時完畢、API資料獲取完畢、事件被觸發。當我們執行setTimeout這個函式時,JavaScript會把給定的函式與像是倒數的秒數之類的附帶資訊(meta data)推送到Event Table裡面,等到一秒過後(目的達成)該函式教會被正式推送到事件除列等待執行,而在這之前JavaScript就是透過Event Table知道有那些事件要被送到事件儲列中。
那麼,什麼又是事件迴圈Event Loop呢?可以把Event Loop想成是另外一個幾乎無時無刻、每一毫秒都在執行的程式,它負責檢查現在主執行環境堆疊是否是空的。如果是空的,再去檢查Event Queue,若Event Queue有函式等待執行,則將這些函式從Event Queue依序到主執行環境並執行。
也因為有事件迴圈與事件儲列的機制,像是事件監聽與透過Ajax拉取資料這類行為才有辦法被達成。例如在監聽網頁點時,一旦使用者做了點擊的動作,對應的邏輯就會被推送到事件儲列內,而事件迴圈看到儲列內有待執行的任務,就會負責去執行。
那麼,現在了解了整個事件儲列的概念,讓我們再度回到setTimeout的例子,來看看一道經典的面試問題。
for (var i=0; i<3; i++){
setTimeout(function(){
console.log(i)
}, 1000)
}
各位可以先想一下,在一秒之後,setTiomeout函式內console.log的輸出的結果是甚麼。這邊用到的概念跟前面講閉包的時候類似,都與先後順序有關。若沒有非同步概念的話,通常會下意識的認為結果會是0、1、2,不過這段程式碼最後其實是會印出3、3、3,不知道有沒有猜對。
搭配識見儲列的概念,現在我們知道setTimeout對應的函式會在主執行環境結束之後才被執行,而等到該函式被執行時,for迴圈裡面的i早就已經因為迴圈而被修改為3了。
要改變三個3的結果,想要看到0、1、2的話該怎麼辦呢?這邊要結合一點前面說到的範疇與閉包的觀念,可以看到for迴圈裡面的i是利用var所宣告,而既然var具有的範疇是根據函式來界定,那麼我只要利用函式,是不是就是夠透過產生閉包,把每個迴圈的i值保留下來呢?當然可以,我們可以利用立即執行函式(IIFE),並把i導入這個立即執行函式,就能夠產生閉包了。
for (var i=0; i<3; i++)
(function(x) {
setTimeout(function(){
console.log(x)
})(i)
}
或者是把var宣告改成let來做宣告
for (let i=0; i<3; i++){
setTimeout(function(){
console.log(i)
}, 1000)
}
我們在前一個段落提到,為了防止網頁的主程式因為等待某些邏輯運算的回應而停擺,有時候JavaScript的行為需要透過非同步的方式來執行,包括一些瀏覽器的API和對外部伺服器拉取資料的動作,而這些動作是利用瀏覽器的Event Queue來達成非同步的行為。
這的確減少不必要的等待,進而增加了使用流程上的順暢度。不過就程式碼的撰寫方式來看,由於在做這些非同步行為的時候,若不做任何的處理,一般都是以回呼函式的方式來進行,才能確保某一段邏輯在非同步行為完成之後才被執行,若這類邏輯開始複雜的時候,可能會變得難以閱讀。
舉前例講到的例子來說,若以setTimeout來模擬一個非同步的行為,想要確保這件事情的話,我們就必須把一個非同步行為放到另外一個非同步的行為的回呼函式裡面。當這樣子的需求越來越多時,你可能就會看到這樣的程式碼。
const asynActionA = (fn)=>setTimeout(fn, 10000);
const asynActionB = (fn)=>setTimeout(fn, 10000);
const asynActionC = (fn)=>setTimeout(fn, 10000);
const asynActionD = (fn)=>setTimeout(fn, 10000);
asyncActionA(()=>{
console.log("asynActionA");
asynActionB(()=>{
console.log("asynActionB");
asynActionC(()=>{
console.log("asynActionC");
asynActionD(()=>{
console.log("asynActionD");
});
});
});
});
這種巢狀的回呼函式一旦多了起來,就容易造成維護上的困難。一般在正式的專案中都非常不樂見這樣子的程式碼。為了解決這樣的問題,我們就必須認識這個Promise。
Promise是什麼呢?Promise是JavaScript版本E86以後出現的新語法,這個詞以字面上的意義來看,用比較白話的方式解釋的話有一種:我承諾幫你做某件事情,能不能成功還不一定,但是我做完之後會把結果告訴你的意思。官方的文件描述則是:
Promise是一個代表非同步運作的最終狀態的物件(成功或失敗)
A Promise is an object repressnting the eventual completion
or failure of an asynchronous operation.(MDN)
從技術文件的角度來解釋就顯得比較抽象,不過你應該大致能夠看出一點頭緒,只要抓住幾個關鍵字--也就是「成功」與「失敗」兩種狀態。
進一步總結以上的論點,一個Promise以時間順序來看是有狀態之分的。除了前面講的成功與失敗兩種結果,一般以Pending來描述在執行中,懸而未決的Promise,一個Promise總共會有三種可能的狀態,分別代表進行中、成功與失敗。而相對於Pending這個代表處理中的狀態,不管是進入成功或失敗狀態的Promise,我們都能夠用Settled來表示這個Promise已經被解決了。
*Pending:還在執行中的狀態,表示還沒有特定結果。
*Fulfilled:成功的狀態,代表Promise被實現,對應的回呼函式為resolve。
*Rejected:失敗的狀態,代表Promise被拒絕,對應的回呼函式為reject。
*Settled:表示Promise已經被解決,結果已經確定。
從前面文件的描述應該也可以看得出來Promise在JavaScript裡面是以物件的方式存在。這個物件又是怎麼產生呢?接下來我們就來看看要怎麼使用Promise吧!基本的Promise宣告方式如下:
我們進一步仔細看一下這個傳進Promise建構函式,它接收了兩個參數:resolve和reject來命名。
resolve和reject 其實分別是兩個具有不同目的的函式。resolve(解析)被用於在認為Promise內的行為成功時呼叫;reject(拒絕)則用於被認為失敗的邏輯發生時呼叫。使用這兩個詞最為函式的名稱只因為這是一種約定俗成,許多人都用這些詞來稱呼它們。