René's URL Explorer Experiment


Title: 手写 Promise 源码实现 · Issue #85 · sisterAn/JavaScript-Algorithms · GitHub

Open Graph Title: 手写 Promise 源码实现 · Issue #85 · sisterAn/JavaScript-Algorithms

X Title: 手写 Promise 源码实现 · Issue #85 · sisterAn/JavaScript-Algorithms

Description: 贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 JS 基础之异步(三):Promise源码实现 查看 完整代码实现 // 三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" function MyPromise(callback) { var _this = this _this.currentState = PENDING ...

Open Graph Description: 贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 JS 基础之异步(三):Promise源码实现 查看 完整代码实现 // 三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" function MyPromise(callback) { var _t...

X Description: 贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 JS 基础之异步(三):Promise源码实现 查看 完整代码实现 // 三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" functio...

Opengraph URL: https://github.com/sisterAn/JavaScript-Algorithms/issues/85

X: @github

direct link

Domain: github.com


Hey, it has json ld scripts:
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"手写 Promise 源码实现","articleBody":"贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 [JS 基础之异步(三):Promise源码实现](https://github.com/sisterAn/blog/issues/13) 查看\r\n\r\n### 完整代码实现\r\n```js\r\n// 三种状态\r\nconst PENDING = \"pending\"\r\nconst FULFILLED = \"fulfilled\"\r\nconst REJECTED = \"rejected\"\r\nfunction MyPromise(callback) {\r\n    var _this = this\r\n    _this.currentState = PENDING // Promise当前的状态\r\n    _this.value = void 0 // Promise的值\r\n    // 用于保存 then 的回调, 只有当 promise\r\n    // 状态为 pending 时才会缓存,并且每个实例至多缓存一个\r\n    _this.onResolvedCallbacks = [] // Promise resolve时的回调函数集\r\n    _this.onRejectedCallbacks = [] // Promise reject时的回调函数集\r\n    _this.resolve = function (value) {\r\n        if (value instanceof MyPromise) {\r\n            // 如果 value 是个 Promise, 递归执行\r\n            return value.then(_this.resolve, _this.reject)\r\n        }\r\n        setTimeout(() =\u003e { // 异步执行,保证顺序执行\r\n            if (_this.currentState === PENDING) {\r\n                _this.currentState = FULFILLED // 状态管理\r\n                _this.value = value\r\n                _this.onResolvedCallbacks.forEach(cb =\u003e cb())\r\n            }\r\n        })\r\n    } // resolve 处理函数\r\n    _this.reject = function (value) {\r\n        setTimeout(() =\u003e { // 异步执行,保证顺序执行\r\n            if (_this.currentState === PENDING) {\r\n                _this.currentState = REJECTED // 状态管理\r\n                _this.value = value\r\n                _this.onRejectedCallbacks.forEach(cb =\u003e cb())\r\n            }\r\n        })\r\n    } // reject 处理函数\r\n\r\n    // 异常处理\r\n    // new Promise(() =\u003e throw Error('error'))\r\n    try {\r\n        callback(_this.resolve, _this.reject) // 执行callback并传入相应的参数\r\n    } catch(e) {\r\n        _this.reject(e)\r\n    }\r\n}\r\n// then 方法接受两个参数,onFulfilled,onRejected,分别为Promise成功或失败的回调\r\nMyPromise.prototype.then = function(onFulfilled, onRejected) {\r\n    var _this = this\r\n    // 规范 2.2.7,then 必须返回一个新的 promise\r\n    var promise2\r\n    // 根据规范 2.2.1 ,onFulfilled、onRejected 都是可选参数\r\n    // onFulfilled、onRejected不是函数需要忽略,同时也实现了值穿透\r\n    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value =\u003e value\r\n    onRejected = typeof onRejected === 'function' ? onRejected : error =\u003e {throw error}\r\n\r\n    if (_this.currentState === FULFILLED) {\r\n        // 如果promise1(此处为self/this)的状态已经确定并且为fulfilled,我们调用onFulfilled\r\n        // 如果考虑到有可能throw,所以我们将其包在try/catch块中\r\n        return promise2 = new MyPromise(function(resolve, reject) {\r\n            try {\r\n                var x = onFulfilled(_this.value)\r\n                // 如果 onFulfilled 的返回值是一个 Promise 对象,直接取它的结果作为 promise2 的结果\r\n                resolutionProcedure(promise2, x, resolve, reject)\r\n            } catch (err) {\r\n                reject(err) // 如果出错,以捕获到的错误作为promise2的结果\r\n            }\r\n        })\r\n    }\r\n    // 此处实现与FULFILLED相似,区别在使用的是onRejected而不是onFulfilled\r\n    if (_this.currentState === REJECTED) {\r\n        return promise2 = new MyPromise(function(resolve, reject) {\r\n            try {\r\n                var x = onRejected(_this.value)\r\n                resolutionProcedure(promise2, x, resolve, reject)\r\n            } catch(err) {\r\n                reject(err)\r\n            }\r\n        })\r\n    }\r\n    if (_this.currentState === PENDING) {\r\n        // 如果当前的Promise还处于PENDING状态,我们并不能确定调用onFulfilled还是onRejected\r\n        // 只有等待Promise的状态确定后,再做处理\r\n        // 所以我们需要把我们的两种情况的处理逻辑做成callback放入promise1(此处即_this/this)的回调数组内\r\n        // 处理逻辑和以上相似\r\n        return promise2 = new MyPromise(function(resolve, reject) {\r\n            _this.onResolvedCallbacks.push(function() {\r\n                try {\r\n                    var x = onFulfilled(_this.value)\r\n                    resolutionProcedure(promise2, x, resolve, reject)\r\n                } catch(err) {\r\n                    reject(err)\r\n                }\r\n            })\r\n            _this.onRejectedCallbacks.push(function() {\r\n                try {\r\n                    var x = onRejected(_this.value)\r\n                    resolutionProcedure(promise2, x, resolve, reject)\r\n                } catch (err) {\r\n                    reject(err)\r\n                }\r\n            })\r\n        })\r\n    }\r\n\r\n    // 规范 2.3\r\n    /*\r\n    resolutionProcedure函数即为根据x的值来决定promise2的状态的函数\r\n    也即标准中的[Promise Resolution Procedure](https://promisesaplus.com/#point-47)\r\n    x 为 promise2 = promise1.then(onFulfilled, onRejected)里onFulfilled/onRejected的返回值\r\n    resolve 和 reject 实际上是 promise2 的executor的两个实参,因为很难挂在其他地方,所以一并传过来。\r\n    相信各位一定可以对照标准转换成代码,这里就只标出代码在标准中对应的位置,只在必要的地方做一些解释。\r\n    */\r\n    function resolutionProcedure(promise2, x, resolve, reject) {\r\n        // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用\r\n        if (promise2 === x) {\r\n            return reject(new TypeError(\"Chaining cycle detected for promise!\"))\r\n        }\r\n        // 规范 2.3.2\r\n        // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行\r\n        if (x instanceof MyPromise) {\r\n            // 2.3.2.1 如果x为pending状态,promise必须保持pending状态,直到x为fulfilled/rejected\r\n            if (x.currentState === PENDING) {\r\n                x.then(function(value) {\r\n                    // 再次调用该函数是为了确认 x resolve 的\r\n                    // 参数是什么类型,如果是基本类型就再次 resolve\r\n                    // 把值传给下个 then\r\n                    resolutionProcedure(promise2, value, resolve, reject)\r\n                }, reject)\r\n            } else { // 但如果这个promise的状态已经确定了,那么它肯定有一个正常的值,而不是一个thenable,所以这里可以取它的状态\r\n                x.then(resolve, reject)\r\n            }\r\n            return\r\n        }\r\n\r\n        let called = false\r\n        // 规范 2.3.3,判断 x 是否为对象或函数\r\n        if (x !== null \u0026\u0026 (typeof x === \"object\" || typeof x === \"function\")) {\r\n            // 规范 2.3.3.2,如果不能取出 then,就 reject\r\n            try {\r\n                // 规范2.3.3.1 因为x.then可能是一个getter,这种情况下多次读取就有可能产生副作用\r\n                // 既要判断它的类型,又要调用它,这就是两次读取\r\n                let then = x.then\r\n                // 规范2.3.3.3,如果 then 是函数,调用 x.then\r\n                if (typeof then === \"function\") {\r\n                    // 规范 2.3.3.3\r\n                    // reject 或 reject 其中一个执行过的话,忽略其他的\r\n                    then.call(\r\n                        x,\r\n                        y =\u003e { // 规范 2.3.3.3.1\r\n                            if (called) return // 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准\r\n                            called = true\r\n                            // 规范 2.3.3.3.1\r\n                            return resolutionProcedure(promise2, y, resolve, reject)\r\n                        },\r\n                        r =\u003e {\r\n                            if (called) return // 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准\r\n                            called = true\r\n                            return reject(r)\r\n                        }\r\n                    )\r\n                } else {\r\n                    // 规范 2.3.3.4\r\n                    resolve(x)\r\n                }\r\n            } catch (e) { // 规范 2.3.3.2\r\n                if (called) return // 规范 2.3.3.3.3,即这三处谁先执行就以谁的结果为准\r\n                called = true\r\n                return reject(e)\r\n            }\r\n        } else {\r\n            // 规范 2.3.4,x 为基本类型\r\n            resolve(x)\r\n        }\r\n    }\r\n}\r\n// catch 的实现\r\nMyPromise.prototype.catch = function (onRejected) {\r\n    return this.then(null, onRejected)\r\n}\r\n// finally 的实现\r\nMyPromise.prototype.finally = function (callback) {\r\n  return this.then(function (value) {\r\n    return MyPromise.resolve(callback()).then(function () {\r\n      return value\r\n    })\r\n  }, function (err) {\r\n    return MyPromise.resolve(callback()).then(function () {\r\n      throw err\r\n    })\r\n  })\r\n}\r\n// race\r\nMyPromise.race = function(values) {\r\n    return new MyPromise(function(resolve, reject) {\r\n        values.forEach(function(value) {\r\n            MyPromise.resolve(value).then(resolve, reject)\r\n        })\r\n    })\r\n}\r\n// all\r\nMyPromise.all = function(arr) {\r\n    var args = Array.prototype.slice.call(arr)\r\n    return new MyPromise(function (resolve, reject) {\r\n        if (args.length === 0) return resolve([])\r\n        var remaining = args.length\r\n        for (var i = 0; i \u003c args.length; i++) {\r\n            res(i, args[i])\r\n        }\r\n        function res(i, val) {\r\n            if (val \u0026\u0026 (typeof val === 'object' || typeof val === 'function')) {\r\n                if (val instanceof MyPromise \u0026\u0026 val.then === MyPromise.prototype.then) {\r\n                    if (val.currentState === FULFILLED) return res(i, val.value)\r\n                    if (val.currentState === REJECTED) reject(val.value)\r\n                    val.then(function (val) {\r\n                        res(i, val)\r\n                    }, reject)\r\n                    return\r\n                } else {\r\n                    var then = val.then\r\n                    if (typeof then === 'function') {\r\n                        var p = new MyPromise(then.bind(val))\r\n                        p.then(function(val) {\r\n                            res(i, val)\r\n                        }, reject)\r\n                        return\r\n                    }\r\n                }\r\n            }\r\n            args[i] = val\r\n            if (--remaining === 0) {\r\n                resolve(args)\r\n            }\r\n        }\r\n    })\r\n}\r\n// allSettled\r\nMyPromise.allSettled = function (promises) {\r\n    return new MyPromise((resolve, reject) =\u003e {\r\n      promises = Array.isArray(promises) ? promises : []\r\n      let len = promises.length\r\n      const argslen = len\r\n      // 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象\r\n      if (len === 0) return resolve([])\r\n      // 将传入的参数转化为数组,赋给args变量\r\n      let args = Array.prototype.slice.call(promises)\r\n      // 计算当前是否所有的 promise 执行完成,执行完毕则resolve\r\n      const compute = () =\u003e {\r\n        if(--len === 0) { \r\n          resolve(args)\r\n        }\r\n      }\r\n      function resolvePromise(index, value) {\r\n        // 判断传入的是否是 promise 类型\r\n        if(value instanceof MyPromise) { \r\n          const then = value.then\r\n          then.call(value, function(val) {\r\n            args[index] = { status: 'fulfilled', value: val}\r\n            compute()\r\n          }, function(e) {\r\n            args[index] = { status: 'rejected', reason: e }\r\n            compute()\r\n          })\r\n        } else {\r\n          args[index] = { status: 'fulfilled', value: value}\r\n          compute()\r\n        }\r\n      }\r\n   \r\n      for(let i = 0; i \u003c argslen; i++){\r\n        resolvePromise(i, args[i])\r\n      }\r\n    })\r\n }\r\n```","author":{"url":"https://github.com/sisterAn","@type":"Person","name":"sisterAn"},"datePublished":"2020-07-19T15:40:58.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":0},"url":"https://github.com/85/JavaScript-Algorithms/issues/85"}

route-pattern/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)
route-controllervoltron_issues_fragments
route-actionissue_layout
fetch-noncev2:f76510c1-624a-287b-f7ac-70e951e0384b
current-catalog-service-hash81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114
request-id8924:1CEAED:1C3C00F:2542B83:696AF76D
html-safe-nonce65fed72d3e4c37850a2442e83e98a92271b334ef90b05f06d488c6f18fa90170
visitor-payloadeyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4OTI0OjFDRUFFRDoxQzNDMDBGOjI1NDJCODM6Njk2QUY3NkQiLCJ2aXNpdG9yX2lkIjoiODMxMjM1NTU2NDcxMjI5NDI1MyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9
visitor-hmac5b92f50cc2a7e1b9c141174fb386c1ba936503bc53855dc083b7663593e4b320
hovercard-subject-tagissue:660905954
github-keyboard-shortcutsrepository,issues,copilot
google-site-verificationApib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I
octolytics-urlhttps://collector.github.com/github/collect
analytics-location///voltron/issues_fragments/issue_layout
fb:app_id1401488693436528
apple-itunes-appapp-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/sisterAn/JavaScript-Algorithms/85/issue_layout
twitter:imagehttps://opengraph.githubassets.com/3bb9321ea3faf969482f737e0493caa9e9d7ff4f81149f530ef2a40562eb92c6/sisterAn/JavaScript-Algorithms/issues/85
twitter:cardsummary_large_image
og:imagehttps://opengraph.githubassets.com/3bb9321ea3faf969482f737e0493caa9e9d7ff4f81149f530ef2a40562eb92c6/sisterAn/JavaScript-Algorithms/issues/85
og:image:alt贴一次我之前写的实现,篇幅太长,这里只贴出实现结果,实现细节可前往 JS 基础之异步(三):Promise源码实现 查看 完整代码实现 // 三种状态 const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" function MyPromise(callback) { var _t...
og:image:width1200
og:image:height600
og:site_nameGitHub
og:typeobject
og:author:usernamesisterAn
hostnamegithub.com
expected-hostnamegithub.com
None5f99f7c1d70f01da5b93e5ca90303359738944d8ab470e396496262c66e60b8d
turbo-cache-controlno-preview
go-importgithub.com/sisterAn/JavaScript-Algorithms git https://github.com/sisterAn/JavaScript-Algorithms.git
octolytics-dimension-user_id19721451
octolytics-dimension-user_loginsisterAn
octolytics-dimension-repository_id252061924
octolytics-dimension-repository_nwosisterAn/JavaScript-Algorithms
octolytics-dimension-repository_publictrue
octolytics-dimension-repository_is_forkfalse
octolytics-dimension-repository_network_root_id252061924
octolytics-dimension-repository_network_root_nwosisterAn/JavaScript-Algorithms
turbo-body-classeslogged-out env-production page-responsive
disable-turbofalse
browser-stats-urlhttps://api.github.com/_private/browser/stats
browser-errors-urlhttps://api.github.com/_private/browser/errors
release82560a55c6b2054555076f46e683151ee28a19bc
ui-targetfull
theme-color#1e2327
color-schemelight dark

Links:

Skip to contenthttps://github.com/sisterAn/JavaScript-Algorithms/issues/85#start-of-content
https://github.com/
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FsisterAn%2FJavaScript-Algorithms%2Fissues%2F85
GitHub CopilotWrite better code with AIhttps://github.com/features/copilot
GitHub SparkBuild and deploy intelligent appshttps://github.com/features/spark
GitHub ModelsManage and compare promptshttps://github.com/features/models
MCP RegistryNewIntegrate external toolshttps://github.com/mcp
ActionsAutomate any workflowhttps://github.com/features/actions
CodespacesInstant dev environmentshttps://github.com/features/codespaces
IssuesPlan and track workhttps://github.com/features/issues
Code ReviewManage code changeshttps://github.com/features/code-review
GitHub Advanced SecurityFind and fix vulnerabilitieshttps://github.com/security/advanced-security
Code securitySecure your code as you buildhttps://github.com/security/advanced-security/code-security
Secret protectionStop leaks before they starthttps://github.com/security/advanced-security/secret-protection
Why GitHubhttps://github.com/why-github
Documentationhttps://docs.github.com
Bloghttps://github.blog
Changeloghttps://github.blog/changelog
Marketplacehttps://github.com/marketplace
View all featureshttps://github.com/features
Enterpriseshttps://github.com/enterprise
Small and medium teamshttps://github.com/team
Startupshttps://github.com/enterprise/startups
Nonprofitshttps://github.com/solutions/industry/nonprofits
App Modernizationhttps://github.com/solutions/use-case/app-modernization
DevSecOpshttps://github.com/solutions/use-case/devsecops
DevOpshttps://github.com/solutions/use-case/devops
CI/CDhttps://github.com/solutions/use-case/ci-cd
View all use caseshttps://github.com/solutions/use-case
Healthcarehttps://github.com/solutions/industry/healthcare
Financial serviceshttps://github.com/solutions/industry/financial-services
Manufacturinghttps://github.com/solutions/industry/manufacturing
Governmenthttps://github.com/solutions/industry/government
View all industrieshttps://github.com/solutions/industry
View all solutionshttps://github.com/solutions
AIhttps://github.com/resources/articles?topic=ai
Software Developmenthttps://github.com/resources/articles?topic=software-development
DevOpshttps://github.com/resources/articles?topic=devops
Securityhttps://github.com/resources/articles?topic=security
View all topicshttps://github.com/resources/articles
Customer storieshttps://github.com/customer-stories
Events & webinarshttps://github.com/resources/events
Ebooks & reportshttps://github.com/resources/whitepapers
Business insightshttps://github.com/solutions/executive-insights
GitHub Skillshttps://skills.github.com
Documentationhttps://docs.github.com
Customer supporthttps://support.github.com
Community forumhttps://github.com/orgs/community/discussions
Trust centerhttps://github.com/trust-center
Partnershttps://github.com/partners
GitHub SponsorsFund open source developershttps://github.com/sponsors
Security Labhttps://securitylab.github.com
Maintainer Communityhttps://maintainers.github.com
Acceleratorhttps://github.com/accelerator
Archive Programhttps://archiveprogram.github.com
Topicshttps://github.com/topics
Trendinghttps://github.com/trending
Collectionshttps://github.com/collections
Enterprise platformAI-powered developer platformhttps://github.com/enterprise
GitHub Advanced SecurityEnterprise-grade security featureshttps://github.com/security/advanced-security
Copilot for BusinessEnterprise-grade AI featureshttps://github.com/features/copilot/copilot-business
Premium SupportEnterprise-grade 24/7 supporthttps://github.com/premium-support
Pricinghttps://github.com/pricing
Search syntax tipshttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
documentationhttps://docs.github.com/search-github/github-code-search/understanding-github-code-search-syntax
Sign in https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2FsisterAn%2FJavaScript-Algorithms%2Fissues%2F85
Sign up https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F%3Cuser-name%3E%2F%3Crepo-name%3E%2Fvoltron%2Fissues_fragments%2Fissue_layout&source=header-repo&source_repo=sisterAn%2FJavaScript-Algorithms
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/85
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/85
Reloadhttps://github.com/sisterAn/JavaScript-Algorithms/issues/85
sisterAn https://github.com/sisterAn
JavaScript-Algorithmshttps://github.com/sisterAn/JavaScript-Algorithms
Notifications https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Fork 649 https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Star 5.7k https://github.com/login?return_to=%2FsisterAn%2FJavaScript-Algorithms
Code https://github.com/sisterAn/JavaScript-Algorithms
Issues 158 https://github.com/sisterAn/JavaScript-Algorithms/issues
Pull requests 0 https://github.com/sisterAn/JavaScript-Algorithms/pulls
Actions https://github.com/sisterAn/JavaScript-Algorithms/actions
Projects 0 https://github.com/sisterAn/JavaScript-Algorithms/projects
Security Uh oh! There was an error while loading. Please reload this page. https://github.com/sisterAn/JavaScript-Algorithms/security
Please reload this pagehttps://github.com/sisterAn/JavaScript-Algorithms/issues/85
Insights https://github.com/sisterAn/JavaScript-Algorithms/pulse
Code https://github.com/sisterAn/JavaScript-Algorithms
Issues https://github.com/sisterAn/JavaScript-Algorithms/issues
Pull requests https://github.com/sisterAn/JavaScript-Algorithms/pulls
Actions https://github.com/sisterAn/JavaScript-Algorithms/actions
Projects https://github.com/sisterAn/JavaScript-Algorithms/projects
Security https://github.com/sisterAn/JavaScript-Algorithms/security
Insights https://github.com/sisterAn/JavaScript-Algorithms/pulse
New issuehttps://github.com/login?return_to=https://github.com/sisterAn/JavaScript-Algorithms/issues/85
New issuehttps://github.com/login?return_to=https://github.com/sisterAn/JavaScript-Algorithms/issues/85
手写 Promise 源码实现https://github.com/sisterAn/JavaScript-Algorithms/issues/85#top
手写源码https://github.com/sisterAn/JavaScript-Algorithms/issues?q=state%3Aopen%20label%3A%22%E6%89%8B%E5%86%99%E6%BA%90%E7%A0%81%22
https://github.com/sisterAn
https://github.com/sisterAn
sisterAnhttps://github.com/sisterAn
on Jul 19, 2020https://github.com/sisterAn/JavaScript-Algorithms/issues/85#issue-660905954
JS 基础之异步(三):Promise源码实现https://github.com/sisterAn/blog/issues/13
手写源码https://github.com/sisterAn/JavaScript-Algorithms/issues?q=state%3Aopen%20label%3A%22%E6%89%8B%E5%86%99%E6%BA%90%E7%A0%81%22
https://github.com
Termshttps://docs.github.com/site-policy/github-terms/github-terms-of-service
Privacyhttps://docs.github.com/site-policy/privacy-policies/github-privacy-statement
Securityhttps://github.com/security
Statushttps://www.githubstatus.com/
Communityhttps://github.community/
Docshttps://docs.github.com/
Contacthttps://support.github.com?tags=dotcom-footer

Viewport: width=device-width


URLs of crawlers that visited me.