aboutsummaryrefslogtreecommitdiff
path: root/files/zh-tw/web/javascript/reference/global_objects/promise/then/index.html
blob: 19682d01995c1429c84dbe2b64b00bac79e7f926 (plain)
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
---
title: Promise.prototype.then()
slug: Web/JavaScript/Reference/Global_Objects/Promise/then
translation_of: Web/JavaScript/Reference/Global_Objects/Promise/then
---
<div>{{JSRef}}</div>

<p><strong><code>then()</code> </strong>方法回傳一個 {{domxref("Promise")}} 物件。它接收兩個引數: <code>Promise</code> 在成功及失敗情況時的回呼函式。</p>

<div class="note">
<p>如果有一個或兩個引數被省略,或為非函式(non-functions),則 <code>then</code> 將處於遺失 handler(s) 的狀態,但不會產生錯誤。若發起 <code>then</code> 之 <code>Promise</code> 採取了一個狀態(實現(<code>fulfillment)</code>或拒絕(<code>rejection))</code>而 <code>then</code> 沒有處理它的函式,一個不具有額外 handlers 的新 <code>Promise</code> 物件將被建立,單純採取原 <code>Promise</code> 其最終狀態。</p>
</div>

<h2 id="語法">語法</h2>

<pre class="syntaxbox"><var>p.then(onFulfilled[, onRejected])</var>;

p.then(function(value) {
  // fulfillment
}, function(reason) {
  // rejection
});
</pre>

<h3 id="參數">參數</h3>

<dl>
 <dt><code>onFulfilled</code></dt>
 <dd>一個 {{jsxref("Function")}},當 <code>Promise</code> 被實現(fulfilled)時被呼叫。此函式接收一個實現值(<code>fullfillment value)作為引數。</code></dd>
 <dt><code>onRejected </code>{{optional_inline}}</dt>
 <dd>一個 {{jsxref("Function")}},當 <code>Promise</code> 被拒絕(rejected)時被呼叫。此函式接收一個失敗訊息(<code>rejection reason)作為引數。</code></dd>
</dl>

<h3 id="回傳值">回傳值</h3>

<p>一個進入<strong>擱置(pending)</strong>狀態的 {{jsxref("Promise")}}。(只要堆疊一空)handler 函式<strong>非同步地(asynchronously)</strong>被呼叫。在調用 handler 後,若 handler 函式:</p>

<ul>
 <li>回傳一個值,則 <code>then</code> 回傳之 promise 以此值被實現(resolved)。</li>
 <li>拋出一個例外,則 <code>then</code> 回傳之 promise 以此例外被否決(rejected)。</li>
 <li>回傳一個被實現的 promise,則 <code>then</code> 回傳之 promise 以此值被實現。</li>
 <li>回傳一個被否決的 promise,則 <code>then</code> 回傳之 promise 以此值被否決。</li>
 <li>回傳另一個被<strong>擱置</strong>的 promise 物件,則 <code>then</code> 回傳之 promise 之實現/拒絕隨後由處理函式之實現/否決決定。並且,<code>then</code> 回傳之 promise 將與處理函式回傳之 promise 以相同值被解決。</li>
</ul>

<p>以下例子展示 <code>then</code> 方法的非同步性質(asynchronicity)。</p>

<pre class="brush: js">// 使用一個已實現的 promise,'then' 區塊將立即被觸發,但是它的 handlers 將是非同步地被觸發,如同 console.logs 所示
var resolvedProm = Promise.resolve(33);

var thenProm = resolvedProm.then(function(value){
    console.log("我在 main stack 之後被呼叫。收到及將回傳的值為:" + value);
    return value;
});
// 立即紀錄 thenProm
console.log(thenProm);

// 我們可以使用 setTimeout 以延遲(postpone)函式執行直到堆疊為空
setTimeout(function(){
    console.log(thenProm);
});


// 紀錄結果,依序為:
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "我在 main stack 之後被呼叫。收到及將回傳的值為:33"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}
</pre>

<h2 id="描述">描述</h2>

<p>因為 <code>then</code> 和 {{jsxref("Promise.prototype.catch()")}} 方法都回傳 promises,它們可以被串接 — 稱為組合(<em>composition)。</em></p>

<h2 id="範例">範例</h2>

<h3 id="運用_then_方法">運用 <code>then</code> 方法</h3>

<pre class="brush: js">var p1 = new Promise( (resolve, reject) =&gt; {
  resolve('Success!');
  // or
  // reject ("Error!");
} );

p1.then( value =&gt; {
  console.log(value); // Success!
}, reason =&gt; {
  console.log(reason); // Error!
} );
</pre>

<h3 id="串接">串接</h3>

<p><code>then</code> 方法回傳一個 <code>Promise</code> 而可以進行方法串接(method chaining)。</p>

<p>如果傳入 <code>then</code> 的 handler 函式回傳一個 promise,一個等價的 <code>Promise</code> 將被展現給方法串接中的下一個 then 。以下程式碼片段透過 <code>setTimout</code> 函式模擬非同步程式碼。</p>

<pre class="brush: js">Promise.resolve('foo')
  // 1. Receive "foo" concatenate "bar" to it and resolve that to the next then
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
  // 2. receive "foobar", register a callback function to work on that string
  // and print it to the console, but not before return the unworked on
  // string to the next then
  .then(function(string) {
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    return string;
  })
  // 3. print helpful messages about how the code in this section will be run
  // before string is actually processed by the mocked asynchronous code in the
  // prior then block.
  .then(function(string) {
    console.log("Last Then:  oops... didn't bother to instantiate and return " +
                "a promise in the prior then so the sequence may be a bit " +
                "surprising");

    // Note that `string` will not have the 'baz' bit of it at this point. This
    // is because we mocked that to happen asynchronously with a setTimeout function
    console.log(string);
  });</pre>

<p>當 handler 僅回傳一個值,實際上它將回傳 <code>Promise.resolve(&lt;value returned by whichever handler was called&gt;)</code>.</p>

<pre class="brush: js">var p2 = new Promise(function(resolve, reject) {
  resolve(1);
});

p2.then(function(value) {
  console.log(value); // 1
  return value + 1;
}).then(function(value) {
  console.log(value + '- This synchronous usage is virtually pointless'); // 2- This synchronous usage is virtually pointless
});

p2.then(function(value) {
  console.log(value); // 1
});
</pre>

<p>若函式拋出一個錯誤或回傳一個被否決的 Promise,<code>then</code> 也將回傳一個被否決的 Promise。</p>

<pre class="brush: js">Promise.resolve()
  .then( () =&gt; {
    // 使 .then() 回傳一個被否決的 Promise
    throw 'Oh no!';
  })
  .then( () =&gt; {
    console.log( 'Not called.' );
  }, reason =&gt; {
    console.error( 'onRejected function called: ', reason );
  });</pre>

<p>在所有其他情形,實現中的 Promise 被回傳。在以下例子中,第一個 <code>then()</code> 將回傳一個實現中包裹 42 的 promise,即使串接中的前一個 Promise 被否決。</p>

<pre class="brush: js">Promise.reject()
  .then( () =&gt; 99, () =&gt; 42 ) // onRejected returns 42 which is wrapped in a resolving Promise
  .then( solution =&gt; console.log( 'Resolved with ' + solution ) ); // Resolved with 42</pre>

<p>實務上,使用 <code>catch</code> 捕捉被否決的 promise 較理想的,而不建議使用兩個引數 <code>then</code> 語法,如下展示。</p>

<pre class="brush: js">Promise.resolve()
  .then( () =&gt; {
    // Makes .then() return a rejected promise
    throw 'Oh no!';
  })
  .catch( reason =&gt; {
    console.error( 'onRejected function called: ', reason );
  })
  .then( () =&gt; {
    console.log( "I am always called even if the prior then's promise rejects" );
  });</pre>

<p><br>
 你也可以透過串接實作一個 Promise-based API 函式,基於它本身。</p>

<pre class="brush: js">function fetch_current_data() {
  // The <a href="/en-US/docs/Web/API/GlobalFetch/fetch">fetch</a>() API returns a Promise.  This function
  // exposes a similar API, except the fulfillment
  // value of this function's Promise has had more
  // work done on it.
  return fetch('current-data.json').then((response) =&gt; {
    if (response.headers.get('content-type') != 'application/json') {
      throw new TypeError();
    }
    var j = response.json();
    // maybe do something with j
    return j; // fulfillment value given to user of
              // fetch_current_data().then()
  });
}
</pre>

<p>若 <code>onFulfilled</code> 回傳一個 promise,則 <code>then</code> 的實現/否決將取決它。</p>

<pre class="brush: js">function resolveLater(resolve, reject) {
  setTimeout(function () {
    resolve(10);
  }, 1000);
}
function rejectLater(resolve, reject) {
  setTimeout(function () {
    reject(20);
  }, 1000);
}

var p1 = Promise.resolve('foo');
var p2 = p1.then(function() {
  // Return promise here, that will be resolved to 10 after 1 second
  return new Promise(resolveLater);
});
p2.then(function(v) {
  console.log('resolved', v);  // "resolved", 10
}, function(e) {
  // not called
  console.log('rejected', e);
});

var p3 = p1.then(function() {
  // Return promise here, that will be rejected with 20 after 1 second
  return new Promise(rejectLater);
});
p3.then(function(v) {
  // not called
  console.log('resolved', v);
}, function(e) {
  console.log('rejected', e); // "rejected", 20
});
</pre>

<h2 id="規範">規範</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">Specification</th>
   <th scope="col">Status</th>
   <th scope="col">Comment</th>
  </tr>
  <tr>
   <td>{{SpecName('ES2015', '#sec-promise.prototype.then', 'Promise.prototype.then')}}</td>
   <td>{{Spec2('ES2015')}}</td>
   <td>Initial definition in an ECMA standard.</td>
  </tr>
  <tr>
   <td>{{SpecName('ESDraft', '#sec-promise.prototype.then', 'Promise.prototype.then')}}</td>
   <td>{{Spec2('ESDraft')}}</td>
   <td> </td>
  </tr>
 </tbody>
</table>

<h2 id="瀏覽器相容性">瀏覽器相容性</h2>

<p class="hidden">To contribute to this compatibility data, please write a pull request against this file: <a href="https://github.com/mdn/browser-compat-data/blob/master/javascript/promise.json">https://github.com/mdn/browser-compat-data/blob/master/javascript/promise.json</a>.</p>

<p>{{Compat("javascript/promise","Promise.prototype.then")}}</p>

<h2 id="參見">參見</h2>

<ul>
 <li>{{jsxref("Promise")}}</li>
 <li>{{jsxref("Promise.prototype.catch()")}}</li>
</ul>