Promise链

  • 异步任务需要一个接一个的执行(如执行脚本),所以可以使用Promise链
new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); 

}).then(function(result) { 

  alert(result); 
  return result * 2;

}).then(function(result) { 

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});
  • 是按照new promise() -> .then -> .then...的顺序执行,只有在上一个完全执行完毕之后,才可以执行下一个
  • 从技术上讲,我们也可以将多个 .then 添加到一个 promise 上。但这并不是 promise 链(chaining)。
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

  • .then中的处理程序可以创建并返回一个promise
  • 其它处理程序要等待它settled后再获得其结果
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

});

示例:loadScript

  • 按顺序依次加载脚本
loadScript("/article/promise-chaining/one.js")
  .then(function(script) {
    return loadScript("/article/promise-chaining/two.js");
  })
  .then(function(script) {
    return loadScript("/article/promise-chaining/three.js");
  })
  .then(function(script) {
    // 使用在脚本中声明的函数
    // 以证明脚本确实被加载完成了
    one();
    two();
    three();
  });
  • 用箭头函数来重写
loadScript("/article/promise-chaining/one.js")
  .then(script => loadScript("/article/promise-chaining/two.js"))
  .then(script => loadScript("/article/promise-chaining/three.js"))
  .then(script => {
    // 脚本加载完成,我们可以在这儿使用脚本中声明的函数
    one();
    two();
    three();
  });
  • 如果向每个loadScript中直接添加.then,会出现“厄运金字塔”现象
  • 所以一般不用下面的写法,链式是首选
loadScript("/article/promise-chaining/one.js").then(script1 => {
  loadScript("/article/promise-chaining/two.js").then(script2 => {
    loadScript("/article/promise-chaining/three.js").then(script3 => {
      // 此函数可以访问变量 script1,script2 和 script3
      one();
      two();
      three();
    });
  });
});

Thenables
  • 处理程序返回的不完全是一个promise,而是返回被称为"thenable"对象(一个具有方法.then的任意对象),它会被当做一个promise来对待
  • 第三方库可以实现自己的"promise 兼容"对象。它们可以具有扩展的方法集,但也与原生的promise兼容,因为它们实现了.then方法
  • 这个特性允许我们将自定义的对象与 promise 链集成在一起,而不必继承自 Promise
class Thenable {
  constructor(num) {
    this.num = num;
  }
  then(resolve, reject) {
    alert(resolve); // function() { native code }
    // 1 秒后使用 this.num*2 进行 resolve
    setTimeout(() => resolve(this.num * 2), 1000);
  }
}

new Promise(resolve => resolve(1))
  .then(result => {
    return new Thenable(result); 
  })
  .then(alert); // 1000ms 后显示 2

fetch

  • promise 通常被用于网络请求
  • fetch方法从远程服务器加载用户信息

let promise = fetch(url);

  • 向url发出网络请求返回一个promise。当远程服务器返回 header(是在 全部响应加载完成前)时,该 promise 使用一个 response 对象来进行 resolve。

向user.json发送请求,并从服务器加载该文本

fetch('/article/promise-chaining/user.json')
  // 当远程服务器响应时,下面的 .then 开始执行
  .then(function(response) {
    // 当 user.json 加载完成时,response.text() 会返回一个新的 promise
    // 该 promise 以加载的 user.json 为 result 进行 resolve
    return response.text();
  })
  .then(function(text) {
    // ...这是远程文件的内容
    alert(text); // {"name": "iliakan", "isAdmin": true}
  });

fetch返回的response对象还包括response.json()方法,该方法读取远程数据并将其解析为 JSON

// 同上,但是使用 response.json() 将远程内容解析为 JSON
fetch('/article/promise-chaining/user.json')
  .then(response => response.json())
  .then(user => alert(user.name)); // iliakan, got user name

案例:向GitHub发送一个请求,加载用户个人资料并显示头像
// 发送一个对 user.json 的请求
fetch('/article/promise-chaining/user.json')
  // 将其加载为 JSON
  .then(response => response.json())
  // 发送一个到 GitHub 的请求
  .then(user => fetch(`https://api.github.com/users/${user.name}`))
  // 将响应加载为 JSON
  .then(response => response.json())
  // 显示头像图片(githubUser.avatar_url)3 秒(也可以加上动画效果)
  .then(githubUser => {
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => img.remove(), 3000); // (*)
  });

添加在头像移除之后显示一个用于编辑该用户或者其它内容的表单

fetch('/article/promise-chaining/user.json')
  .then(response => response.json())
  .then(user => fetch(`https://api.github.com/users/${user.name}`))
  .then(response => response.json())
  //new Promise()
  .then(githubUser => new Promise(function(resolve, reject) { // (*)
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => {
      img.remove();
      resolve(githubUser); // 返回该用户的名字
    }, 3000);
  }))
  
  .then(githubUser => alert(`Finished showing ${githubUser.name}`));
  • 因为上面的程序按照链式依次调用,只有在setTimeout中的resolve(githubUser) 被调用后才会变为 settled.
  • 更好的做法是将异步行为封装成一个函数,该函数始终返回一个promise
  • 如下:我们可以将代码拆分成可重用的函数
function loadJson(url) {
  return fetch(url)
    .then(response => response.json());
}

function loadGithubUser(name) {
  return fetch(`https://api.github.com/users/${name}`)
    .then(response => response.json());
}

function showAvatar(githubUser) {
  return new Promise(function(resolve, reject) {
    let img = document.createElement('img');
    img.src = githubUser.avatar_url;
    img.className = "promise-avatar-example";
    document.body.append(img);

    setTimeout(() => {
      img.remove();
      resolve(githubUser);
    }, 3000);
  });
}

// 使用它们:
loadJson('/article/promise-chaining/user.json')
  .then(user => loadGithubUser(user.name))
  .then(showAvatar)
  .then(githubUser => alert(`Finished showing ${githubUser.name}`));
  // ...

总结

如果 .then(或 catch/finally 都可以)处理程序返回一个 promise,那么链的其余部分将会等待,直到它状态变为 settled。当它被 settled 后,其 result(或 error)将被进一步传递下去。
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值