使用Promise将异步转化为同步执行

PROMISE

promise是一个容器,保存着某个未来才会结束的事件(异步操作)的结果。

promise有三种状态:pending(进行中)、fulfilled(成功)和reject(失败),只有异步操作的结果可以改变promise的状态。一旦promise从pending状态转化为fulfilled或reject后就不会再改变。

  • 1. 新建一个Promise

Promise在新建后就会立即执行,Promise.then()方法会在当前脚本所有同步任务执行完后才会执行

const promise = new Promise(function(resolve, reject) {
  //...异步操作
  if (/* 异步操作成功 */){
    resolve(value); 	// resolve将Promise的状态由pending变为resolved
  } else {
    reject(error);		// reject将Promise的状态由pending变为失败
  }
});
  • 2. 用then方法指定resolve和reject状态的处理方法

  1. then的两个参数分别是resolve和reject的回调函数,reject回调不是必须的
  2. then方法在异步操作完成后,将Promise的状态修改为resolve或reject后才会触发对应的回调函数
promise.then(function(value) {
  // 异步操作执行成功后的处理
}, function(error) {
  // 异步操作失败的处理
});
  • 3. 回调函数的参数

如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。

resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例

p1p2都是 Promise 的实例,但是p2resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作。

注意,这时p1的状态就会传递给p2,也就是说,p1的状态决定了p2的状态。如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行

const p1 = new Promise(function (resolve, reject) {
  //三秒后返回reject
  setTimeout(() => reject(new Error('fail')), 3000)	//reject参数为错误信息ERROR
})

const p2 = new Promise(function (resolve, reject) {
  //p1的状态决定了p2的状态,后续语句便针对p1的状态
  setTimeout(() => resolve(p1), 1000)
})

//由于p1返回reject,p1又决定了p2的状态,因此触发了.catch 打印error
p2
  .then(result => console.log(result))
  .catch(error => console.log(error))

4. Promise的then方法

then方法返回的是一个新的Promise对象,因此可以采用链式写法。使用链式的写法, 可以指定一组按照次序的回调函数。

axios.get('“异步请求”')
  .then(function(reslut) {
  	//获取返回结果reslut
  	return reslut
	})
  .then(function(reslut) {
  	// 执行得到返回结果后相应的操作
	});

5. Promise的catch方法

catch方法用于指定发生错误(reject)时的回调函数

let promise = new Promise(
  function(resolve, reject) {
    throw new Error('test'); //reject
  }
);
promise
  .then(function(){
  //正常情况的回调resolve
	})
	.catch(function(){
  //异常情况的回调reject:test
})

6. Promise的finally方法

无论Promise最后状态如何都会执行的方法,在ES2018引入。

finally()不接受任何参数,在finally里的方法与状态无关,finally本质上是then的特例。如果不使用finally,相当于在then和catch的方法里将finally的方法各写一次。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

7. Promise.all()方法

将多个Promise实例包装为一个新的Promise实例。

Promise.all()接受一个数组作为参数,p1,p2, p3都是Promise实例。

const p = Promise.all([p1, p2, p3])

p的状态由p1,p2,p3决定,只有p1p2p3的状态都变成fulfilledp的状态才会变成fulfilled,此时p1p2p3的返回值组成一个数组,传递给p的回调函数。

只要p1p2p3之中有一个被rejectedp的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

const databasePromise = connectDatabase();

const booksPromise = databasePromise
  .then(findAllBooks);

const userPromise = databasePromise
  .then(getCurrentUser);

Promise.all([
  booksPromise,
  userPromise
])
.then(([books, user]) => pickTopRecommendations(books, user));

8. Promise.race()

const p = Promise.race([p1, p2, p3]);

Race()与all方法参数一致,但是race只要p1p2p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

9.Promise.resolve()

将现有对象转化为Promise对象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。

(1)参数是一个 Promise 实例

如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。

(2)参数是一个thenable对象

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

(3)参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved

(4)不带有任何参数

Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

10. Promise.reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了

上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。

注意,Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

上面代码中,Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。

================================================================

在JavaScript中需要在数组中循环调用异步方法,又需要等待上一轮异步方法的执行结果才能继续,因此我们需要使用Promise来完成调用异步方法的顺序执行。

方式一

let usePromise = async()=>{
  let result = [], array = [1,2,3,4] // array作为循环遍历用
 	let pro
  
  //开始循环遍历
  array.forEach(element=>{
    pro = new Promise(resolve=>{
      //setTimeout代表异步方法,等待一秒后执行
			setTimeout(()=>{
        result.push(element);
        resolve();
      }, 1000);      
    })
  })
  await pro;
  console.log(result); 	// [1,2,3,4]
}

方式二

let array1 = [1]

function prom1 (){
  return new Promise(function(resovle){
    setTimeout(()=>{
      array1.push('a');
      resovle(array1);
    }, 1000);
  })
}

function prom2 (){
  return new Promise(function(resovle){
    setTimeout(()=>{
      array1.push('b');
      resovle(array1);
    }, 1000);
  })
}

function prom3 (){
  return new Promise(function(resovle){
    console.log(array1)
  })
}

let pro = new Promise(function(resolve, reject){
  resolve();
})

pro.then(prom1)
	 .then(prom2)
	 .then(prom3)	// [1, a, b]

方式三

以下代码功能: 获取一个URL数组,通过for循环依次访问数组中的各个URL地址,进入各个URL页面的子页面获取数据,当一个页面的所有数据都获取到并处理完毕,才会进入下一个for循环的异步请求访问新的URL页面请求数据。

let array =[1,2,3,4];
let p = Promise.resolve();

for (let i = 0; i < array.length; i ++){
  p = p.then(_=>new Promise(resolve=>{
    //异步方法,发送http的GET请求获取数据
    superagent.get(bookUrl[i]).end(async(err, res) => {
                if (err) {
                    console.log("err",err);
                    return
                } else {
                    let $ = cheerio.load(res.text);
                    bookName.push($('div.book-info').find('h1').find('em').text())
                    let readURL = $('div.book-info').find('a#readBtn').attr('href');
                  			//异步方法,获取数据中的请求路由
                        superagent.get("https:"+readURL).end((err, res)=>{
                            if(err){
                                console.log(err);
                              	//reject(); 使用reject需要在new Promise传入该参数,并有.catch方法
                            }else{
                                let $ = cheerio.load(res.text);
                                content.push($('div.read-content').find('p').text()); 
                                if($('div.read-content').find('p').text()!=="")  {
                                  	// 当resolve时此次方法调用才算完成,才会进入下次循环发送交易
                                    resolve(content)
                                }  
                            }
                        })

                }
            })
  	})) 
}

p.then(function(){
  doSomething();
})

=============================>>>
//大体框架
let p = Promise.resolve();
array.forEach((value,index, array)=>{
  p= p.then(_=>{new Promise(resolve)=>{
    doSomething({
      ...
      resolve();
    })
  }})
})	
p.then(function(){
  doSomething...
})


  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript中,我们可以使用一些方法将异步代码换为同步执行的代码。一种常见的方法是使用async/await关键字,它可以让异步代码看起来像同步代码一样运行。通过使用async关键字定义一个函数,我们可以在函数内部使用await关键字来等待异步操作的结果。这样可以确保异步操作在执行完成之前不会继续执行下面的代码。 下面是一个示例,演示了如何将一个异步的延迟函数delayAsync换为同步执行的方法delaySync: ``` function delayAsync(ms) { return new Promise((resolve, reject) => { setTimeout(() => { resolve('Done'); }, ms); }); } async function delaySync(ms) { try { const result = await delayAsync(ms); console.log(result); // 继续处理结果 } catch (error) { console.error(error); // 处理错误 } } delaySync(2000); ``` 在上面的示例中,delayAsync函数返回一个Promise对象,通过使用setTimeout函数模拟一个延迟操作。在delaySync函数中,我们使用await关键字等待delayAsync函数的结果,并在控制台打印出结果。通过这种方式,我们可以在代码中以同步的方式处理异步操作的结果。 需要注意的是,将异步方法换为同步执行的方法应该谨慎使用异步方法的非阻塞特性通常是其优势,所以只有在确实需要同步执行的情况下才应该进行换。在不必要的情况下,应避免将异步方法换为同步执行,以充分利用异步操作的性能优势。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [JavaScript编程技巧:将异步方法换为同步执行的实用方法](https://blog.csdn.net/qq_39997939/article/details/131063612)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值