JS - Promise

JS - Promise使用詳解--摘抄筆記

 

第一部分:html

JS - Promise使用詳解1(基本概念、使用優勢)

1、promises相關概念

promises 的概念是由 CommonJS 小組的成員在 Promises/A 規範中提出來的。
 

1,then()方法介紹

根據 Promise/A 規範,promise 是一個對象,只須要 then 這一個方法。then 方法帶有以下三個參數:
  • 成功回調
  • 失敗回調
  • 前進回調(規範沒有要求包括前進回調的實現,可是不少都實現了)。
一個全新的 promise 對象從每一個 then 的調用中返回。
 

2,Promise對象狀態

Promise 對象表明一個異步操做,其不受外界影響,有三種狀態:
  • Pending(進行中、未完成的)
  • Resolved(已完成,又稱 Fulfilled)
  • Rejected(已失敗)。
(1)promise 從未完成的狀態開始,若是成功它將會是完成態,若是失敗將會是失敗態。
(2)當一個 promise 移動到完成態,全部註冊到它的成功回調將被調用,並且會將成功的結果值傳給它。另外,任何註冊到 promise 的成功回調,將會在它已經完成之後當即被調用。
(3)一樣的,當一個 promise 移動到失敗態的時候,它調用的是失敗回調而不是成功回調。
(4)對包含前進特性的實現來講,promise 在它離開未完成狀態之前的任什麼時候刻,均可以更新它的 progress。當 progress 被更新,全部的前進回調(progress callbacks)會被傳遞以 progress 的值,並被當即調用。前進回調被以不一樣於成功和失敗回調的方式處理;若是你在一個 progress 更新已經發生之後註冊了一個前進回調,新的前進回調只會在它被註冊之後被已更新的 progress 調用。
(5)注意:只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。
 

3,Promise/A規範圖解

原文:JS - Promise使用詳解1(基本概念、使用優勢)

4,目前支持Promises/A規範的庫

  • Q:能夠在NodeJS 以及瀏覽器上工做,與jQuery兼容,能夠經過消息傳遞遠程對象。
  • RSVP.js:一個輕量級的庫,它提供了組織異步代碼的工具。
  • when.js:體積小巧,使用方便。
  • NodeJS的Promise
  • jQuery 1.5:聽說是基於「CommonJS Promises/A」規範
  • WinJS / Windows 8 / Metro
 

2、使用promises的優點

1,解決回調地獄(Callback Hell)問題

(1)有時咱們要進行一些相互間有依賴關係的異步操做,好比有多個請求,後一個的請求須要上一次請求的返回結果。過去常規作法只能 callback 層層嵌套,但嵌套層數過多的話就會有 callback hell 問題。好比下面代碼,可讀性和維護性都不好的。
1
2
3
4
5
6
7
8
9
10
11
12
firstAsync( function (data){
     //處理獲得的 data 數據
     //....
     secondAsync( function (data2){
         //處理獲得的 data2 數據
         //....
         thirdAsync( function (data3){
               //處理獲得的 data3 數據
               //....
         });
     });
});
 

(2)若是使用 promises 的話,代碼就會變得扁平且更可讀了。前面提到 then 返回了一個 promise,所以咱們能夠將 then 的調用不停地串連起來。其中 then 返回的 promise 裝載了由調用返回的值。ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
firstAsync()
.then( function (data){
     //處理獲得的 data 數據
     //....
     return  secondAsync();
})
.then( function (data2){
     //處理獲得的 data2 數據
     //....
     return  thirdAsync();
})
.then( function (data3){
     //處理獲得的 data3 數據
     //....
});

 

2,更好地進行錯誤捕獲 

多重嵌套 callback 除了會形成上面講的代碼縮進問題,更可怕的是可能會形成沒法捕獲異常或異常捕獲不可控。
(1)好比下面代碼咱們使用 setTimeout 模擬異步操做,在其中拋出了個異常。但因爲異步回調中,回調函數的執行棧與原函數分離開,致使外部沒法抓住異常。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function  fetch(callback) {
     setTimeout(() => {
         throw  Error( '請求失敗' )
     }, 2000)
}
 
try  {
     fetch(() => {
         console.log( '請求處理' // 永遠不會執行
     })
catch  (error) {
     console.log( '觸發異常' , error)  // 永遠不會執行
}
 
// 程序崩潰
// Uncaught Error: 請求失敗
 
(2)若是使用 promises 的話,經過 reject 方法把 Promise 的狀態置爲 rejected,這樣咱們在 then 中就能捕捉到,而後執行「失敗」狀況的回調。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function  fetch(callback) {
     return  new  Promise((resolve, reject) => {
         setTimeout(() => {
              reject( '請求失敗' );
         }, 2000)
     })
}
 
 
fetch()
.then(
     function (data){
         console.log( '請求處理' );
         console.log(data);
     },
     function (reason, data){
         console.log( '觸發異常' );
         console.log(reason);
     }
);

固然咱們在 catch 方法中處理 reject 回調也是能夠的。
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function  fetch(callback) {
     return  new  Promise((resolve, reject) => {
         setTimeout(() => {
              reject( '請求失敗' );
         }, 2000)
     })
}
 
 
fetch()
.then(
     function (data){
         console.log( '請求處理' );
         console.log(data);
     }
)
. catch ( function (reason){
     console.log( '觸發異常' );
     console.log(reason);
});

第二部分:數組

JS - Promise使用詳解2(ES6中的Promise)

2015年6月, ES2015(即 ECMAScript 六、ES6) 正式發佈。其中 Promise 被列爲正式規範,成爲 ES6 中最重要的特性之一。promise

 

1,then()方法

簡單來說,then 方法就是把原來的回調寫法分離出來,在異步操做執行完後,用鏈式調用的方式執行回調函數。
而 Promise 的優點就在於這個鏈式調用。咱們能夠在 then 方法中繼續寫 Promise 對象並返回,而後繼續調用 then 來進行回調操做。
 
(1)下面經過樣例做爲演示,咱們定義作飯、吃飯、洗碗(cook、eat、wash)這三個方法,它們是層層依賴的關係,下一步的的操做須要使用上一部操做的結果。(這裏使用 setTimeout 模擬異步操做)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
//作飯
function  cook(){
     console.log( '開始作飯。' );
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '作飯完畢!' );
             resolve( '雞蛋炒飯' );
         }, 1000);
     });
     return  p;
}
 
//吃飯
function  eat(data){
     console.log( '開始吃飯:'  + data);
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '吃飯完畢!' );
             resolve( '一塊碗和一雙筷子' );
         }, 2000);
     });
     return  p;
}
 
function  wash(data){
     console.log( '開始洗碗:'  + data);
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '洗碗完畢!' );
             resolve( '乾淨的碗筷' );
         }, 2000);
     });
     return  p;
}


(2)使用 then 鏈式調用這三個方法:瀏覽器

1
2
3
4
5
6
7
8
9
10
cook()
.then( function (data){
     return  eat(data);
})
.then( function (data){
     return  wash(data);
})
.then( function (data){
     console.log(data);
});


固然上面代碼還能夠簡化成以下:異步

1
2
3
4
5
6
cook()
.then(eat)
.then(wash)
.then( function (data){
     console.log(data);
});


(3)運行結果以下:函數

原文:JS - Promise使用詳解2(ES6中的Promise)

2,reject()方法

上面樣例咱們經過 resolve 方法把 Promise 的狀態置爲完成態(Resolved),這時 then 方法就能捕捉到變化,並執行「成功」狀況的回調。
而 reject 方法就是把 Promise 的狀態置爲已失敗(Rejected),這時 then 方法執行「失敗」狀況的回調(then 方法的第二參數)。
 
(1)下面一樣使用一個樣例作演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//作飯
function  cook(){
     console.log( '開始作飯。' );
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '作飯失敗!' );
             reject( '燒焦的米飯' );
         }, 1000);
     });
     return  p;
}
 
//吃飯
function  eat(data){
     console.log( '開始吃飯:'  + data);
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '吃飯完畢!' );
             resolve( '一塊碗和一雙筷子' );
         }, 2000);
     });
     return  p;
}
 
cook()
.then(eat,  function (data){
   console.log(data +  '無法吃!' );
})

運行結果以下:工具

原文:JS - Promise使用詳解2(ES6中的Promise)
(2)若是咱們只要處理失敗的狀況可使用 then(null, ...),或是使用接下來要講的 catch 方法。
1
2
3
4
cook()
.then( null function (data){
   console.log(data +  '無法吃!' );
})

 

3,catch()方法

(1)它能夠和 then 的第二個參數同樣,用來指定 reject 的回調post

1
2
3
4
5
cook()
.then(eat)
. catch ( function (data){
     console.log(data +  '無法吃!' );
});


(2)它的另外一個做用是,當執行 resolve 的回調(也就是上面 then 中的第一個參數)時,若是拋出異常了(代碼出錯了),那麼也不會報錯卡死 js,而是會進到這個 catch 方法中。fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//作飯
function  cook(){
     console.log( '開始作飯。' );
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '作飯完畢!' );
             resolve( '雞蛋炒飯' );
         }, 1000);
     });
     return  p;
}
 
//吃飯
function  eat(data){
     console.log( '開始吃飯:'  + data);
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '吃飯完畢!' );
             resolve( '一塊碗和一雙筷子' );
         }, 2000);
     });
     return  p;
}
 
cook()
.then( function (data){
     throw  new  Error( '米飯被打翻了!' );
     eat(data);
})
. catch ( function (data){
     console.log(data);
});

運行結果以下:

原文:JS - Promise使用詳解2(ES6中的Promise)
這種錯誤的捕獲是很是有用的,由於它可以幫助咱們在開發中識別代碼錯誤。好比,在一個 then() 方法內部的任意地方,咱們作了一個 JSON.parse() 操做,若是 JSON 參數不合法那麼它就會拋出一個同步錯誤。用回調的話該錯誤就會被吞噬掉,可是用 promises 咱們能夠輕鬆的在 catch() 方法裏處理掉該錯誤。
 
(3)還能夠添加多個 catch,實現更加精準的異常捕獲。
1
2
3
4
5
6
7
8
9
10
11
somePromise.then( function () {
  return  a();
}). catch (TypeError,  function (e) {
  //If a is defined, will end up here because
  //it is a type error to reference property of undefined
}). catch (ReferenceError,  function (e) {
  //Will end up here if a wasn't defined at all
}). catch ( function (e) {
  //Generic catch-the rest, error wasn't TypeError nor
  //ReferenceError
});
 

4,all()方法

Promise 的 all 方法提供了並行執行異步操做的能力,而且在全部異步操做執行完後才執行回調。
 
(1)好比下面代碼,兩個個異步操做是並行執行的,等到它們都執行完後纔會進到 then 裏面。同時 all 會把全部異步操做的結果放進一個數組中傳給 then。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//切菜
function  cutUp(){
     console.log( '開始切菜。' );
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '切菜完畢!' );
             resolve( '切好的菜' );
         }, 1000);
     });
     return  p;
}
 
//燒水
function  boil(){
     console.log( '開始燒水。' );
     var  p =  new  Promise( function (resolve, reject){         //作一些異步操做
         setTimeout( function (){
             console.log( '燒水完畢!' );
             resolve( '燒好的水' );
         }, 1000);
     });
     return  p;
}
 
Promise
.all([cutUp(), boil()])
.then( function (results){
     console.log( "準備工做完畢:" );
     console.log(results);
});


(2)運行結果以下:

原文:JS - Promise使用詳解2(ES6中的Promise)

5,race()方法

race 按字面解釋,就是賽跑的意思。race 的用法與 all 同樣,只不過 all 是等全部異步操做都執行完畢後才執行 then 回調。而 race 的話只要有一個異步操做執行完畢,就馬上執行 then 回調。
注意:其它沒有執行完畢的異步操做仍然會繼續執行,而不是中止。
 
(1)這裏咱們將上面樣例的 all 改爲 race
1
2
3
4
5
6
Promise
.race([cutUp(), boil()])
.then( function (results){
     console.log( "準備工做完畢:" );
     console.log(results);
});
原文:JS - Promise使用詳解2(ES6中的Promise)
 
(2)race 使用場景不少。好比咱們能夠用 race 給某個異步請求設置超時時間,而且在超時後執行相應的操做。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//請求某個圖片資源
function  requestImg(){
     var  p =  new  Promise( function (resolve, reject){
     var  img =  new  Image();
     img.onload =  function (){
        resolve(img);
     }
     img.src =  'xxxxxx' ;
     });
     return  p;
}
 
//延時函數,用於給請求計時
function  timeout(){
     var  p =  new  Promise( function (resolve, reject){
         setTimeout( function (){
             reject( '圖片請求超時' );
         }, 5000);
     });
     return  p;
}
 
Promise
.race([requestImg(), timeout()])
.then( function (results){
     console.log(results);
})
. catch ( function (reason){
     console.log(reason);
});

上面代碼 requestImg 函數異步請求一張圖片,timeout 函數是一個延時 5 秒的異步操做。咱們將它們一塊兒放在 race 中賽跑。

  • 若是 5 秒內圖片請求成功那麼便進入 then 方法,執行正常的流程。
  • 若是 5 秒鐘圖片還未成功返回,那麼則進入 catch,報「圖片請求超時」的信息。
原文:JS - Promise使用詳解2(ES6中的Promise)

 

第三部分:

JS - Promise使用詳解3(jQuery中的Deferred)

上文我介紹了 ES6 中的 Promise,它徹底遵循 Promises/A 規範。而咱們熟悉的 jQuery 又有本身的 Promise 實現:Deferred(但其並非遵循 Promises/A 規範)。本文就講講 jQuery 中 Promise 的實現。

 

1、Deferred對象及其方法

1,$.Deferred

  • jQuery 用 $.Deferred 實現了 Promise 規範。
  • $.Deferred() 返回一個對象,咱們能夠稱之爲 Deferred 對象,上面掛着一些熟悉的方法如:done、fail、then 等。
  • jQuery 就是用這個 Deferred 對象來註冊異步操做的回調函數,修改並傳遞異步操做的狀態。
 

下面咱們定義作飯、吃飯、洗碗(cook、eat、wash)這三個方法(這裏使用 setTimeout 模擬異步操做)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//作飯
function  cook(){
     console.log( '開始作飯。' );
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '作飯完畢!' );
         def.resolve( '雞蛋炒飯' );
     }, 1000);
     return  def.promise();
}
 
//吃飯
function  eat(data){
     console.log( '開始吃飯:'  + data);
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '吃飯完畢!' );
         def.resolve( '一塊碗和一雙筷子' );
     }, 1000);
     return  def.promise();
}
 
//洗碗
function  wash(data){
     console.log( '開始洗碗:'  + data);
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '洗碗完畢!' );
         def.resolve( '乾淨的碗筷' );
     }, 1000);
     return  def.promise();
}

 

2,then()方法

經過 Deferred 對象的 then 方法咱們能夠實現鏈式調用。
(1)好比上面樣例的三個方法是層層依賴的關係,且下一步的的操做須要使用上一部操做的結果。咱們能夠這麼寫:
1
2
3
4
5
6
7
8
9
10
cook()
.then( function (data){
     return  eat(data);
})
.then( function (data){
     return  wash(data);
})
.then( function (data){
     console.log(data);
});

固然也能夠簡寫成以下:

1
2
3
4
5
6
cook()
.then(eat)
.then(wash)
.then( function (data){
     console.log(data);
});


(2)運行結果以下:

原文:JS - Promise使用詳解3(jQuery中的Deferred)

3,reject()方法

上面樣例咱們經過 resolve 方法把 Deferred 對象的狀態置爲完成態(Resolved),這時 then 方法就能捕捉到變化,並執行「成功」狀況的回調。
而 reject 方法就是把 Deferred 對象的狀態置爲已失敗(Rejected),這時 then 方法執行「失敗」狀況的回調(then 方法的第二參數)。
 
(1)下面一樣使用一個樣例作演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//作飯
function  cook(){
     console.log( '開始作飯。' );
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '作飯完畢!' );
         def.reject( '燒焦的米飯' );
     }, 1000);
     return  def.promise();
}
 
//吃飯
function  eat(data){
     console.log( '開始吃飯:'  + data);
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '吃飯完畢!' );
         def.resolve( '一塊碗和一雙筷子' );
     }, 1000);
     return  def.promise();
}
 
cook()
.then(eat,  function (data){
   console.log(data +  '無法吃!' );
})

運行結果以下:

原文:JS - Promise使用詳解3(jQuery中的Deferred)
(2)Promise 規範中,then 方法接受兩個參數,分別是執行完成和執行失敗的回調。而 jQuery 中進行了加強,還能夠接受第三個參數,就是在 pending(進行中)狀態時的回調。
1
deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

 

4,done()與fail()方法

done 和 fail 是 jQuery 增長的兩個語法糖方法。分別用來指定執行完成和執行失敗的回調。

好比下面兩段代碼是等價的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//then方法
d.then( function (){
     console.log( '執行完成' );
},  function (){
     console.log( '執行失敗' );
});
 
//done方法、fail方法
d.done( function (){
     console.log( '執行完成' );
})
.fail( function (){
     console.log( '執行失敗' );
});

 

5,always()方法

jQuery 的 Deferred 對象上還有一個 always 方法,不論執行完成仍是執行失敗,always 都會執行,有點相似 ajax 中的 complete。

1
2
3
4
5
6
cook()
.then(eat)
.then(wash)
.always( function (){
   console.log( '上班去!' );
})

 

2、與Promises/A規範的差別

在開頭講到,目前 Promise 事實上的標準是社區提出的 Promises/A 規範,jQuery 的實現並不徹底符合 Promises/A,主要表如今對錯誤的處理不一樣。

1,ES6中對錯誤的處理

下面代碼咱們在回調函數中拋出一個錯誤,Promises/A 規定此時 Promise 實例的狀態變爲 reject,同時該錯誤會被下一個 catch 方法指定的回調函數捕獲。
1
2
3
4
5
6
7
8
cook()
.then( function (data){
     throw  new  Error( '米飯被打翻了!' );
     eat(data);
})
. catch ( function (data){
     console.log(data);
});

 

2,jQuery中對錯誤的處理

一樣咱們在回調函數中拋出一個錯誤,jQuery 的 Deferred 對象此時不會改變狀態,亦不會觸發回調函數,該錯誤通常狀況下會被 window.onerror 捕獲。換句話說,在 Deferred 對象中,老是必須使用 reject 方法來改變狀態。

1
2
3
4
5
6
7
8
9
10
cook()
.then( function (data){
     throw  new  Error( '米飯被打翻了!' );
     eat(data);
})
 
window.onerror =  function (msg, url, line) {
     console.log( "發生錯誤了:"  + msg);
     return  true //若是註釋掉該語句,瀏覽器中仍是會有錯誤提示,反之則沒有。
}
原文:JS - Promise使用詳解3(jQuery中的Deferred)

 

3、$.when方法

jQuery 中,還有一個 $.when 方法。它與 ES6 中的 all 方法功能同樣,並行執行異步操做,在全部的異步操做執行完後才執行回調函數。當有兩個地方要注意:
  • $.when 並無定義在 $.Deferred 中,看名字就知道,$.when 它是一個單獨的方法。
  • $.when 與 ES6 的 all 的參數稍有區別,它接受的並非數組,而是多個 Deferred 對象。
 
(1)好比下面代碼,兩個個異步操做是並行執行的,等到它們都執行完後纔會進到 then 裏面。同時 all 會把全部異步操做的結果傳給 then。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//切菜
function  cutUp(){
     console.log( '開始切菜。' );
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '切菜完畢!' );
         def.resolve( '切好的菜' );
     }, 1000);
     return  def.promise();
}
 
//燒水
function  boil(){
     console.log( '開始燒水。' );
     var  def = $.Deferred();
     //執行異步操做
     setTimeout( function (){
         console.log( '燒水完畢!' );
         def.resolve( '燒好的水' );
     }, 1000);
     return  def.promise();
}
 
$.when(cutUp(), boil())
.then( function (data1, data2){
     console.log( "準備工做完畢:" );
     console.log(data1, data2);
});

 

4、Ajax函數與Deferred的關係

jQuery 中咱們經常會用到的 ajax, get, post 等 Ajax 函數,其實它們內部都已經實現了 Deferred。這些方法調用後會返回一個受限的 Deferred 對象。既然是 Deferred 對象,那麼天然也有上面提到的全部特性。

 

1,then方法

好比咱們經過鏈式調用,連續發送多個請求。
1
2
3
4
5
6
7
8
9
10
11
12
13
req1 =  function (){
     return  $.ajax( /*...*/ );
}
req2 =  function (){
     return  $.ajax( /*...*/ );
}
req3 =  function (){
     return  $.ajax( /*...*/ );
}
 
req1().then(req2).then(req3).done( function (){
     console.log( '請求發送完畢' );
});

 

2,success、error與complete方法

success、error、complete是 ajax 提供的語法糖,功能與 Deferred 對象的 done、fail、always 一致。好比下面兩段代碼功能是一致的:

1
2
3
4
5
6
7
8
9
10
11
//使用success、error、complete
$.ajax( /*...*/ )
.success( function (){ /*...*/ })
.error( function (){ /*...*/ })
.complete( function (){ /*...*/ })
 
//使用done、fail、always
$.ajax( /*...*/ )
.done( function (){ /*...*/ })
.fai( function (){ /*...*/ })
.always( function (){ /*...*/ })


原文出自:www.hangge.com  轉載請保留原文連接:http://www.hangge.com/blog/cache/detail_1639.html


原文出自:www.hangge.com  轉載請保留原文連接:http://www.hangge.com/blog/cache/detail_1638.html


原文出自:www.hangge.com  轉載請保留原文連接:http://www.hangge.com/blog/cache/detail_1635.html

相關文章
相關標籤/搜索