javascript promises的使用

javascript

使用promises

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promisess

一个Promise是一个对象,代表一个异步操作最终的成功或失败。由于大部分人是已经创建的promises的消费者,这个指引将先解释如何使用返回(创建好的)promise,然后再解释如何创建。

 

本质上,一个promise是一个异步函数返回的对象,你可以attach回调给这个对象,而不是传递callback函数给异步函数。

 

想象一个函数,createAudioFileAsync(), 它接收参数是一个配置记录和一个成功的回调函数,一个失败的回调函数,然后异步生成一个声音文件。

它的实现是这样的:

function successCallback(result) {
  console.log("Audio file ready at URL: " + result);
}
​
function failureCallback(error) {
  console.error("Error generating audio file: " + error);
}
​
createAudioFileAsync(audioSettings, successCallback, failureCallback);

然而现代语言是返回一个promise,然后你可以attach你的callback.

createAudioFileAsync用promise重写,它就会是如下简单:

createAudioFileAsync(audioSettings).then(successCallback, failureCallback);

它是下面的简写:

const promise = createAudioFileAsync(audioSettings); 
promise.then(successCallback, failureCallback);

上面就是个异步函数调用,下面逐一介绍它的优点。

Guarantees保证

不象老式的传递回调函数,promise的下面几个保证:

  1. 在当前运行JavaScript事件循环的完成之前,永远不会调用回调。

  2. callback通过then来添加,即使是在异步操作完成后(成功或失败),callback也保证会被调用,如前面的例子。

  3. 通过多次调用then来添加多个callback,每个callback会按顺序一个一个地调用。

可以使用chainning是使用promise的最大好处。

Chainning调用链

一个常用的事例是多个异步调用需要一个接一个地调用,后面的操作开始执行基于前面操作返回的结果。 这种我们可以通过promiss chain来完成。

这个魔术的原理是then返回一个新的promise,这是与原来不同的地方。

const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);

也可以写成

const promise2 = doSomething().then(successCallback, failureCallback);  

promise2代表不仅是doSomething完成了,同样代表successCallback或failureCallback的完成,promise2可以是加外一个异步函数返回的promise. 这就是,任何添加在promise2之后的callback都会排在由successCallback or failureCallback返回的promise之后。

基本上,每个promise代表chain上的另外一个异步的完成。

在过去,连续执行几个异步操作会导致经典的回调金字塔:

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

现代的promise chain

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

then的参数是可选的,而catch(failureCallback)then(null, failureCallback)的简写。

可以用lambda表达式.箭头函数

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
  console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);

重要: 必须返回结果,否则其它callbacks获取不到前一个promise的结果。 (with arrow functions () => x is short for () => { return x; }).

一个具体的promise chain:

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
  alert(result); // 1
  return result * 2;
}).then(function(result) { // (***)
  alert(result); // 2
  return result * 2;
}).then(function(result) {
  alert(result); // 4
  return result * 2;
});

分开调then的经典错误,下面是不会链式传递结果。

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
});
​
promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});
​
promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});
​
promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

上面直接返回结果,下面返回的是promise

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
}).then(function(result) {
  alert(result); // 1
  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });
}).then(function(result) { // (**)
  alert(result); // 2
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(result * 2), 1000);
  });
}).then(function(result) {
  alert(result); // 4
});

具体看 https://javascript.info/promise-chaining

Chaining after a catch

用catch处理chain上失败的情况

new Promise((resolve, reject) => {
    console.log('Initial');//run
​
    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this');//not run
})
.catch(() => {
    console.error('Do that');//run
})
.then(() => {
    console.log('Do this, no matter what happened before');//run
});

输出如下:

Initial
Do that
Do this, no matter what happened before

错误传播Error propagation

你可能还记得,在之前末日金字塔(pyramid of doom)有三次失败回调,而在promise chain的末端,只有一次:

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);

如果链上有错误,浏览器就会往链下面 找 .catch() or onRejected

Promises通过捕获所有错误,甚至抛出异常和编程错误,解决了回调金字塔的一个基本缺陷。这对于异步操作的功能组合至关重要。

Promise rejection events

当promise被 rejected时,下面两个event中的一个会发送到全局。(通常是window,或者web worker或者其它基于web worker的interface)

rejectionhandled

reject被处理后

unhandledrejection

reject未被处理

web开发者要学习下,我用nodejs跳过先。

为老的callback api封闭promise

例如旧的

setTimeout(() => saySomething("10 seconds passed"), 10*1000);

用promise

const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
​
wait(10*1000).then(() => saySomething("10 seconds")).catch(failureCallback);

组合Composition

Promise.all()等所有的异步操作并行执行完毕。

Promise.all([func1(), func2(), func3()])
.then(([result1, result2, result3]) => { /* use result1, result2 and result3 */ });

顺序组合

[func1, func2, func3].reduce((p, f) => p.then(f), Promise.resolve())
.then(result3 => { /* use result3 */ });

与下面相等

Promise.resolve().then(func1).then(func2).then(func3);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FatherOfCodingMan

如果觉得有用的话,可以赏点饭钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值