aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/reference/global_objects/promise/then/index.html
blob: c41a74f379f83320977b25057a56c19ce80c6a4e (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
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
---
title: Promise.prototype.then()
slug: Web/JavaScript/Reference/Global_Objects/Promise/then
tags:
  - ECMAScript 2015
  - JavaScript
  - Promise
  - Prototype
  - 方法
translation_of: Web/JavaScript/Reference/Global_Objects/Promise/then
---
<div>{{JSRef}}</div>

<p><code><strong>then()</strong></code> 方法返回一个 {{domxref("Promise")}}。它最多需要有两个参数:Promise 的成功和失败情况的回调函数。</p>

<div>{{EmbedInteractiveExample("pages/js/promise-then.html")}}</div>

<p class="hidden">The source for this interactive demo is stored in a GitHub repository. If you'd like to contribute to the interactive demo project, please clone <a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a> and send us a pull request.</p>

<div class="note">
<p>注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么 <code>then</code> 方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用 <code>then</code><code>Promise</code> 的状态(fulfillment 或 rejection)发生改变,但是 <code>then</code> 中并没有关于这种状态的回调函数,那么 <code>then</code> 将创建一个没有经过回调函数处理的新 <code>Promise</code> 对象,这个新 <code>Promise</code> 只是简单地接受调用这个 <code>then</code> 的原 <code>Promise</code> 的终态作为它的终态。</p>
</div>

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

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

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

<h3 id="参数">参数</h3>

<dl>
 <dt><code>onFulfilled</code> {{optional_inline}}</dt>
 <dd>当 Promise 变成接受状态(fulfilled)时调用的{{jsxref("Function", "函数")}}。该函数有一个参数,即接受的最终结果(the fulfillment  value)。如果该参数不是函数,则会在内部被替换为 <code>(x) =&gt; x</code>,即原样返回 promise 最终结果的函数</dd>
 <dt><code>onRejected</code> {{optional_inline}}</dt>
 <dd>当 Promise 变成拒绝状态(rejected)时调用的{{jsxref("Function", "函数")}}。该函数有一个参数,即拒绝的原因(<code>rejection reason</code>)。  如果该参数不是函数,则会在内部被替换为一个 "Thrower" 函数 (it throws an error it received as argument)。</dd>
</dl>

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

<p>当一个 {{jsxref("Promise")}} 完成(fulfilled)或者失败(rejected)时,返回函数将被异步调用(由当前的线程循环来调度完成)。具体的返回值依据以下规则返回。如果 <code>then</code> 中的回调函数:</p>

<ul>
 <li>返回了一个值,那么 <code>then</code> 返回的 Promise 将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。</li>
 <li>没有返回任何值,那么 <code>then</code> 返回的 Promise 将会成为接受状态,并且该接受状态的回调函数的参数值为 <code>undefined</code></li>
 <li>抛出一个错误,那么 <code>then</code> 返回的 Promise 将会成为拒绝状态,并且将抛出的错误作为拒绝状态的回调函数的参数值。</li>
 <li>返回一个已经是接受状态的 Promise,那么 <code>then</code> 返回的 Promise 也会成为接受状态,并且将那个 Promise 的接受状态的回调函数的参数值作为该被返回的Promise的接受状态回调函数的参数值。</li>
 <li>返回一个已经是拒绝状态的 Promise,那么 <code>then</code> 返回的 Promise 也会成为拒绝状态,并且将那个 Promise 的拒绝状态的回调函数的参数值作为该被返回的Promise的拒绝状态回调函数的参数值。</li>
 <li>返回一个未定状态(<code>pending</code>)的 Promise,那么 <code>then</code> 返回 Promise 的状态也是未定的,并且它的终态与那个 Promise 的终态相同;同时,它变为终态时调用的回调函数参数与那个 Promise 变为终态时的回调函数的参数是相同的。</li>
</ul>

<p>下面是一个演示<code> then</code> 方法的同步性的例子。</p>

<pre class="brush: js">// using a resolved promise, the 'then' block will be triggered instantly,
// but its handlers will be triggered asynchronously as demonstrated by the console.logs
const resolvedProm = Promise.resolve(33);

let thenProm = resolvedProm.then(value =&gt; {
    console.log("this gets called after the end of the main stack. the value received and returned is: " + value);
    return value;
});
// instantly logging the value of thenProm
console.log(thenProm);

// using setTimeout we can postpone the execution of a function to the moment the stack is empty
setTimeout(() =&gt; {
    console.log(thenProm);
});


// 上面的代码会依次返回:
// Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined}
// "this gets called after the end of the main stack. the value received and returned is: 33"
// Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 33}</pre>

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

<p>由于 <code>then</code>{{jsxref("Promise.prototype.catch()")}} 方法都会返回 promise,它们可以被<a href="/zh-CN/docs/Web/JavaScript/Guide/Using_promises#Chaining">链式调用</a>——这同时也是一种被称为<strong>复合</strong>( <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('成功!');
  // or
  // reject(new Error("出错了!"));
});

p1.then(value =&gt; {
  console.log(value); // 成功!
}, reason =&gt; {
  console.error(reason); // 出错了!
});
</pre>

<h3 id="链式调用">链式调用</h3>

<p><font face="Open Sans, sans-serif"><code>then</code> 方法返回</font>一个 <code>Promise</code> 对象,其允许方法链。</p>

<p>你可以传递一个匿名函数给 <code>then</code>,并且,如果它返回一个 <code>Promise</code>,一个等价的 <code>Promise</code> 将暴露给后续的方法链。下面的代码片段使用 <code>setTimout</code> 函数来模拟异步代码操作。</p>

<pre class="brush: js">Promise.resolve("foo")
  // 1. 接收 "foo" 并与 "bar" 拼接,并将其结果做为下一个 resolve 返回。
  .then(function(string) {
    return new Promise(function(resolve, reject) {
      setTimeout(function() {
        string += 'bar';
        resolve(string);
      }, 1);
    });
  })
  // 2. 接收 "foobar", 放入一个异步函数中处理该字符串
  // 并将其打印到控制台中, 但是不将处理后的字符串返回到下一个。
  .then(function(string) {
    setTimeout(function() {
      string += 'baz';
      console.log(string);
    }, 1)
    return string;
  })
  // 3. 打印本节中代码将如何运行的帮助消息,
  // 字符串实际上是由上一个回调函数之前的那块异步代码处理的。
  .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");

    // 注意 `string` 这时不会存在 'baz'。
    // 因为这是发生在我们通过setTimeout模拟的异步函数中。
    console.log(string);
  });

// logs, in order:
// Last Then: oops... didn't bother to instantiate and return a promise in the prior then so the sequence may be a bit surprising
// foobar
// foobarbaz</pre>

<p>当一个值只是从一个 <code>then</code> 内部返回时,它将等价地返回 <code>Promise.resolve(&lt;由被调用的处理程序返回的值&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 + ' - A synchronous value works');
});

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() 返回一个 rejected promise
    throw new Error('Oh no!');
  })
  .then(() =&gt; {
    console.log('Not called.');
  }, error =&gt; {
    console.error('onRejected function called: ' + error.message);
  });</pre>

<p>在其他情况下,一个 resolving Promise 会被返回。在下面的例子里,第一个 <code>then()</code> 会返回一个用 resolving Promise 包装的 <code>42</code>,即使之前的 Promise 是 rejected 的。</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>实际上,捕获 rejected promise 的需求经常大于使用 <code>then</code> 的两种情况语法,比如下面这样的:</p>

<pre class="brush: js">Promise.resolve()
  .then(() =&gt; {
    // 使 .then() 返回一个 rejected promise
    throw new Error('Oh no!');
  })
  .catch(error =&gt; {
    console.error('onRejected function called: ' + error.message);
  })
  .then(() =&gt; {
    console.log("I am always called even if the prior then's promise rejects");
  });</pre>

<p>你也可以在另一个顶层函数上使用链式去实现基于 Promise API 的函数。</p>

<pre class="brush: js">function fetch_current_data() {
  // fetch() API 返回了一个 Promise.
  // 这个函数提供了类似的API,
  // 这个函数除了实现 Promise,它还能够完成更多的工作。
  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> 的返回值就会被 Promise resolved 或者 rejected。</p>

<pre class="brush: js">function resolveLater(resolve, reject) {
  setTimeout(function() {
    resolve(10);
  }, 1000);
}
function rejectLater(resolve, reject) {
  setTimeout(function() {
    reject(new Error('Error'));
  }, 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.error('rejected', e);
});

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

<h3 id="基于_promise_的_domxrefwindow.setImmediate_polyfill">基于 promise 的 {{domxref("window.setImmediate")}} polyfill</h3>

<p>Using a {{jsxref("Function.prototype.bind()")}} <code>Reflect.apply</code> ({{jsxref("Reflect.apply()")}}) method to create a (non-cancellable) setImmediate-style function.</p>

<pre class="brush: js">const nextTick = (() =&gt; {
  const noop = () =&gt; {}; // literally
  const nextTickPromise = () =&gt; Promise.resolve().then(noop);

  const rfab = Reflect.apply.bind; // (thisArg, fn, thisArg, [...args])
  const nextTick = (fn, ...args) =&gt; (
    fn !== undefined
    ? Promise.resolve(args).then(rfab(null, fn, null))
    : nextTickPromise(),
    undefined
  );
  nextTick.ntp = nextTickPromise;

  return nextTick;
})();
</pre>

<h2 id="规范">规范</h2>

<table class="standard-table">
 <tbody>
  <tr>
   <th scope="col">规范</th>
   <th scope="col">状态</th>
   <th scope="col">备注</th>
  </tr>
  <tr>
   <td>{{SpecName('ES2015', '#sec-promise.prototype.then', 'Promise.prototype.then')}}</td>
   <td>{{Spec2('ES2015')}}</td>
   <td>ECMA 标准中的首次定义。</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 repository: <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a>.</p>

<p>{{Compat("javascript.builtins.Promise.then")}}</p>

<h2 id="参见">参见</h2>

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